GuildHelper = GuildHelper or {}

local addon = GuildHelper

addon.NewUIHome = addon.NewUIHome or {}

local cardFallback = {
    Background = { 0.12, 0.12, 0.14, 0.98 },
    Border = { 0.34, 0.34, 0.4, 1 },
    Title = { 0.96, 0.93, 0.84, 1 },
    Value = { 0.72, 0.68, 0.58, 1 },
}

local panelFallback = {
    Background = { 0.12, 0.12, 0.14, 0.98 },
    Border = { 0.34, 0.34, 0.4, 1 },
    Title = { 0.96, 0.93, 0.84, 1 },
}

local footerFallback = {
    Background = { 0.12, 0.12, 0.14, 0.98 },
    Border = { 0.34, 0.34, 0.4, 1 },
    Title = { 0.96, 0.93, 0.84, 1 },
    Value = { 0.72, 0.68, 0.58, 1 },
    RailBackground = { 0.13, 0.11, 0.08, 1 },
    FillPrimary = { 0.62, 0.48, 0.16, 1 },
    FillSecondary = { 0.24, 0.62, 0.34, 1 },
    FillTertiary = { 0.62, 0.48, 0.16, 1 },
}

local homeFallback = {
    raid = "Thu 4:00 PM",
    membersOnline = "1",
    mythicRuns = "23",
    guildNews = "1 New",
}

local CLASS_ICON_TEXTURE = CLASS_ICON_TEXTURE or "Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES"

local function trimString(value)
    return tostring(value or ""):match("^%s*(.-)%s*$") or ""
end

local function formatCompactLocalHomeTime(timestamp)
    local resolvedTimestamp = tonumber(timestamp) or 0

    if resolvedTimestamp <= 0 then
        return ""
    end

    local weekday = date("%a", resolvedTimestamp)
    local hour24 = tonumber(date("%H", resolvedTimestamp)) or 0
    local minute = tonumber(date("%M", resolvedTimestamp)) or 0
    local meridiem = hour24 >= 12 and "PM" or "AM"
    local hour12 = hour24 % 12

    if hour12 == 0 then
        hour12 = 12
    end

    return string.format("%s %d:%02d%s", weekday, hour12, minute, meridiem)
end

local function getLiveUpcomingRaidText()
    if not addon.GroupsService or type(addon.GroupsService.ListGroups) ~= "function" or type(addon.GroupsService.GetUpcomingOccurrences) ~= "function" then
        return nil
    end

    local ok, groups = pcall(addon.GroupsService.ListGroups, addon.GroupsService, { activityType = "raid", sortBy = "start" })

    if not ok or type(groups) ~= "table" then
        return nil
    end

    for _, group in ipairs(groups) do
        local okOccurrences, occurrences = pcall(addon.GroupsService.GetUpcomingOccurrences, addon.GroupsService, group, 1)

        if okOccurrences and type(occurrences) == "table" and type(occurrences[1]) == "table" then
            local occurrence = occurrences[1]
            local compactLocalText = formatCompactLocalHomeTime(occurrence.startAt)

            if compactLocalText ~= "" then
                return compactLocalText
            end

            local displayText = trimString(occurrence.localLabel or occurrence.label or "")

            if displayText ~= "" then
                return displayText
            end
        end
    end

    return nil
end

local function getLiveMembersOnlineText()
    if not addon.RosterService or type(addon.RosterService.GetCounts) ~= "function" then
        return nil
    end

    local ok, counts = pcall(addon.RosterService.GetCounts, addon.RosterService)

    if not ok or type(counts) ~= "table" then
        return nil
    end

    local onlineCount = tonumber(counts.online)

    if onlineCount == nil then
        return nil
    end

    return tostring(math.max(0, math.floor(onlineCount)))
end

local function getLiveMythicRunsText()
    if not addon.MythicPlusService or type(addon.MythicPlusService.GetLeaderboardSummary) ~= "function" then
        return nil
    end

    local ok, summary = pcall(addon.MythicPlusService.GetLeaderboardSummary, addon.MythicPlusService)

    if not ok or type(summary) ~= "table" then
        return nil
    end

    local rowCount = tonumber(summary.rowCount)

    if rowCount == nil then
        return nil
    end

    return tostring(math.max(0, math.floor(rowCount)))
end

local function getLiveGuildNewsValue()
    if not addon.ContentService or type(addon.ContentService.ListVisibleContent) ~= "function" then
        return nil
    end

    local ok, records = pcall(addon.ContentService.ListVisibleContent, addon.ContentService, {
        typeFilter = "announcement",
    })

    if not ok or type(records) ~= "table" then
        return nil
    end

    local newsCount = 0

    for _, record in ipairs(records) do
        if type(record) == "table" and not record.deleted then
            newsCount = newsCount + 1
        end
    end

    if newsCount <= 0 then
        return {
            text = homeFallback.guildNews,
            isLive = false,
            source = "fallback",
        }
    end

    return {
        text = string.format("%d New", newsCount),
        isLive = true,
        source = "ContentService",
    }
end

local function BuildHomeSnapshot(snapshot)
    local resolved = type(snapshot) == "table" and snapshot or {}

    local function resolveCardValue(existingValue, liveValue, fallbackValue, sourceName)
        if type(existingValue) == "table" and type(existingValue.text) == "string" and trimString(existingValue.text) ~= "" then
            return {
                text = existingValue.text,
                isLive = existingValue.isLive and true or false,
                source = trimString(existingValue.source) ~= "" and existingValue.source or sourceName,
            }
        end

        if type(liveValue) == "table" and type(liveValue.text) == "string" and trimString(liveValue.text) ~= "" then
            return {
                text = liveValue.text,
                isLive = liveValue.isLive and true or false,
                source = trimString(liveValue.source) ~= "" and liveValue.source or sourceName,
            }
        end

        if trimString(liveValue) ~= "" then
            return {
                text = tostring(liveValue),
                isLive = true,
                source = sourceName,
            }
        end

        return {
            text = fallbackValue,
            isLive = false,
            source = "fallback",
        }
    end

    local raidValue = resolveCardValue(resolved.raid, getLiveUpcomingRaidText(), homeFallback.raid, "GroupsService")
    local membersValue = resolveCardValue(resolved.membersOnline, getLiveMembersOnlineText(), homeFallback.membersOnline, "RosterService")
    local mythicValue = resolveCardValue(resolved.mythicRuns, getLiveMythicRunsText(), homeFallback.mythicRuns, "MythicPlusService")
    local newsValue = resolveCardValue(resolved.guildNews, getLiveGuildNewsValue(), homeFallback.guildNews, "ContentService")

    resolved.raid = raidValue
    resolved.membersOnline = membersValue
    resolved.mythicRuns = mythicValue
    resolved.guildNews = newsValue

    return resolved
end

local function BuildWeeklyEventsSnapshot(snapshot)
    local resolved = type(snapshot) == "table" and snapshot or {}

    local function resolveEventRow(existingRow, liveRow, sourceName)
        if type(existingRow) == "table" and type(existingRow.text) == "string" and trimString(existingRow.text) ~= "" then
            return {
                text = existingRow.text,
                time = trimString(existingRow.time),
                isLive = existingRow.isLive and true or false,
                source = trimString(existingRow.source) ~= "" and existingRow.source or sourceName,
            }
        end

        if type(liveRow) == "table" and type(liveRow.text) == "string" and trimString(liveRow.text) ~= "" then
            return {
                text = liveRow.text,
                time = trimString(liveRow.time),
                isLive = liveRow.isLive and true or false,
                source = trimString(liveRow.source) ~= "" and liveRow.source or sourceName,
            }
        end

        return nil
    end

    local function getLiveWeeklyEventRows()
            if not addon.GroupsService or type(addon.GroupsService.ListGroups) ~= "function" or type(addon.GroupsService.GetUpcomingOccurrences) ~= "function" then
                return nil
            end

            local okGroups, groups = pcall(addon.GroupsService.ListGroups, addon.GroupsService, {
                sortBy = "start",
            })

            if not okGroups or type(groups) ~= "table" then
                return nil
            end

            local rows = {}

            for _, group in ipairs(groups) do
                if #rows >= 4 then
                    break
                end

                local okOccurrences, occurrences = pcall(addon.GroupsService.GetUpcomingOccurrences, addon.GroupsService, group, 1)

                if okOccurrences and type(occurrences) == "table" and type(occurrences[1]) == "table" then
                    local occurrence = occurrences[1]
                    local title = trimString(group.eventName or group.localLabel or group.label or group.title or group.name or occurrence.title or occurrence.label or "")
                    local time = formatCompactLocalHomeTime(occurrence.startAt)

                    if time == "" then
                        time = trimString(occurrence.localStartText or occurrence.startText or occurrence.timeText or occurrence.localTimeText or occurrence.time or "")
                    end

                    if title ~= "" then
                        rows[#rows + 1] = {
                            text = title,
                            time = time,
                            isLive = true,
                            source = "GroupsService",
                        }
                    end
                end
            end

            return #rows > 0 and rows or nil
        end

        local liveRows = getLiveWeeklyEventRows()
        local rows = {}

        for index = 1, 4 do
            rows[index] = resolveEventRow(resolved.weeklyEvents and resolved.weeklyEvents[index], liveRows and liveRows[index], "GroupsService")
        end

        resolved.weeklyEvents = rows
        return resolved
    end

local function BuildMPlusRaidHighlightsSnapshot(snapshot)
    local resolved = type(snapshot) == "table" and snapshot or {}

    local function normalizeText(value, fallbackValue)
        local text = trimString(value)

        if text ~= "" then
            return text
        end

        return fallbackValue
    end

    local function formatMaybeNumber(value)
        local numericValue = tonumber(value)

        if numericValue ~= nil then
            return tostring(math.floor(numericValue))
        end

        local text = trimString(value)

        if text ~= "" then
            return text
        end

        return "-"
    end

    local function formatMaybeTimed(summary)
        if type(summary) ~= "table" then
            return "-"
        end

        if summary.timedCount == nil then
            return "-"
        end

        return (tonumber(summary.timedCount) or 0) > 0 and "Yes" or "No"
    end

    local function getCurrentGuildMemberLookup()
        if not addon.RosterService or type(addon.RosterService.ListCurrentGuildMembersRaw) ~= "function" then
            return nil
        end

        local okMembers, members = pcall(addon.RosterService.ListCurrentGuildMembersRaw, addon.RosterService)

        if not okMembers or type(members) ~= "table" then
            return nil
        end

        local lookup = {}

        local function addLookupKey(value)
            local text = trimString(value)

            if text == "" then
                return
            end

            lookup[text] = true
            lookup[string.lower(text)] = true
        end

        for _, member in ipairs(members) do
            if type(member) == "table" then
                addLookupKey(member.fullName)
                addLookupKey(member.id)
                addLookupKey(member.name)

                local normalizedMemberId = addon.Store and addon.Store.NormalizeFullName and addon.Store:NormalizeFullName(member.fullName or member.id or member.name, GetRealmName() or "Unknown") or nil

                addLookupKey(normalizedMemberId)
            end
        end

        return next(lookup) ~= nil and lookup or nil
    end

    local function getLiveMythicRows()
        if not addon.MythicPlusService or type(addon.MythicPlusService.ListLeaderboardRows) ~= "function" then
            return nil
        end

        local okRows, leaderboardRows = pcall(addon.MythicPlusService.ListLeaderboardRows, addon.MythicPlusService)

        if not okRows or type(leaderboardRows) ~= "table" then
            return nil
        end

        local rows = {}
        local guildMembers = getCurrentGuildMemberLookup()

        for _, sourceRow in ipairs(leaderboardRows) do
            if #rows >= 2 then
                break
            end

            if type(sourceRow) == "table" then
                local toonId = sourceRow.id or sourceRow.toonId or sourceRow.fullName or sourceRow.shortName
                local toonSummary = nil
                local seasonOverviewSummary = nil
                local playerName = normalizeText(sourceRow.fullName or sourceRow.shortName or sourceRow.name or sourceRow.player or sourceRow.toon or sourceRow.charName or (toonId and tostring(toonId)) or "", "")
                local normalizedPlayerName = playerName ~= "" and (addon.Store and addon.Store.NormalizeFullName and addon.Store:NormalizeFullName(playerName, GetRealmName() or "Unknown") or playerName) or ""

                if playerName ~= "" and (not guildMembers or guildMembers[playerName] or guildMembers[string.lower(playerName)] or guildMembers[normalizedPlayerName] or guildMembers[string.lower(normalizedPlayerName)]) then
                    if toonId and type(addon.MythicPlusService.GetToonSummary) == "function" then
                        local okToon, summary = pcall(addon.MythicPlusService.GetToonSummary, addon.MythicPlusService, toonId)

                        if okToon and type(summary) == "table" then
                            toonSummary = summary
                        end
                    end

                    if toonId and type(addon.MythicPlusService.GetSeasonOverviewSummary) == "function" then
                        local okSeason, summary = pcall(addon.MythicPlusService.GetSeasonOverviewSummary, addon.MythicPlusService, toonId)

                        if okSeason and type(summary) == "table" then
                            seasonOverviewSummary = summary
                        end
                    end

                    rows[#rows + 1] = {
                        player = normalizeText((toonSummary and toonSummary.fullName) or playerName or (toonSummary and toonSummary.id) or sourceRow.name or sourceRow.player or sourceRow.toon or sourceRow.charName, "Unknown"),
                        key = normalizeText(sourceRow.currentKeyText or (toonSummary and toonSummary.currentKeyText) or (toonSummary and toonSummary.currentKeyShortText) or sourceRow.currentKeyLevel or sourceRow.keyLevel or sourceRow.level, "-"),
                        timed = formatMaybeTimed(seasonOverviewSummary),
                        score = formatMaybeNumber(sourceRow.overallRating or (toonSummary and toonSummary.overallRating) or (seasonOverviewSummary and seasonOverviewSummary.overallRating) or sourceRow.score or sourceRow.rating),
                        deaths = "-",
                        isLive = true,
                        source = "MythicPlusService:ListLeaderboardRows",
                    }
                end
            end
        end

        return #rows > 0 and rows or nil
    end

    local function getLiveRaidHighlight()
        if not addon.GroupsService or type(addon.GroupsService.ListGroups) ~= "function" then
            return nil
        end

        local okGroups, groups = pcall(addon.GroupsService.ListGroups, addon.GroupsService, {
            activityType = "raid",
            sortBy = "start",
        })

        if not okGroups or type(groups) ~= "table" then
            return nil
        end

        for _, group in ipairs(groups) do
            if type(group) == "table" then
                local raidName = trimString(group.eventName or group.name)
                local totalSignups = nil
                local qualifiedCount = nil

                if type(addon.GroupsService.GetPlannerData) == "function" then
                    local okPlanner, planner = pcall(addon.GroupsService.GetPlannerData, addon.GroupsService, group.id)
                    if okPlanner and type(planner) == "table" then
                        totalSignups = tonumber(planner.totalSignups)
                        qualifiedCount = tonumber(planner.qualifiedCount)
                    end
                end

                if raidName ~= "" or totalSignups or qualifiedCount then
                    local signupText = nil
                    if totalSignups ~= nil and qualifiedCount ~= nil then
                        signupText = string.format("%d signed / %d ready", math.max(0, math.floor(totalSignups)), math.max(0, math.floor(qualifiedCount)))
                    elseif totalSignups ~= nil then
                        signupText = string.format("%d signed", math.max(0, math.floor(totalSignups)))
                    end

                    return {
                        raidName = raidName ~= "" and raidName or "Scheduled Raid",
                        bossesKilled = signupText or "No signups yet",
                        detailLabel = "Signups",
                        isLive = true,
                        source = "GroupsService",
                    }
                end
            end
        end

        return nil
    end

    local liveRows = getLiveMythicRows()
    local rows = {}

    if liveRows and #liveRows > 0 then
        for index = 1, math.min(#liveRows, 2) do
            rows[index] = liveRows[index]
        end
    end

    local raidRow = getLiveRaidHighlight()

    resolved.mythicRaidHighlights = rows
    resolved.raidHighlight = raidRow
    return resolved
end

local function GetCardTheme()
    if addon.NewUITheme and addon.NewUITheme.Default and addon.NewUITheme.Default.Card then
        return addon.NewUITheme.Default.Card
    end

    if addon.NewUITheme and addon.NewUITheme.Card then
        return addon.NewUITheme.Card
    end

    return cardFallback
end

local theme = GetCardTheme()

local function GetPanelTheme()
    if addon.NewUITheme and addon.NewUITheme.Default and addon.NewUITheme.Default.Panel then
        return addon.NewUITheme.Default.Panel
    end

    if addon.NewUITheme and addon.NewUITheme.Panel then
        return addon.NewUITheme.Panel
    end

    return panelFallback
end

local panelTheme = GetPanelTheme()

local function GetFooterTheme()
    if addon.NewUITheme and addon.NewUITheme.Default and addon.NewUITheme.Default.Footer then
        return addon.NewUITheme.Default.Footer
    end

    if addon.NewUITheme and addon.NewUITheme.Footer then
        return addon.NewUITheme.Footer
    end

    return footerFallback
end

local footerTheme = GetFooterTheme()

local home = addon.NewUIHome
local CreateArtContentFrame

local function ApplyRect(frame, parent, rect)
    if not frame or not parent or not rect then return end
    frame:ClearAllPoints()
    frame:SetPoint("TOPLEFT", parent, "TOPLEFT", rect.x, rect.y)
    frame:SetSize(rect.w, rect.h)
end

local function CreateBackdrop(frame, useTransparentFill)
    if frame.SetBackdrop then
        frame:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Buttons\\WHITE8X8",
            edgeSize = 1,
            insets = {
                left = 1,
                right = 1,
                top = 1,
                bottom = 1,
            },
        })
        if useTransparentFill then
            frame:SetBackdropColor(0, 0, 0, 0)
        else
            frame:SetBackdropColor(0.12, 0.12, 0.14, 0.98)
        end
        if useTransparentFill then
            frame:SetBackdropBorderColor(0, 0, 0, 0)
        else
            frame:SetBackdropBorderColor(0.34, 0.34, 0.4, 1)
        end
        return
    end

    if not useTransparentFill then
        local background = frame:CreateTexture(nil, "BACKGROUND")
        background:SetAllPoints(frame)
        background:SetColorTexture(0.12, 0.12, 0.14, 0.98)
        frame.background = background
    end
end

local function resolveHomeTexturePath(textureKeyOrPath)
    if type(textureKeyOrPath) ~= "string" or textureKeyOrPath == "" then
        return nil
    end

    local textures = addon.NewUIAssets and addon.NewUIAssets.Textures or nil
    if textures and textures[textureKeyOrPath] ~= nil then
        return textures[textureKeyOrPath] ~= "" and textures[textureKeyOrPath] or nil
    end

    return textureKeyOrPath
end

local function resolveHomeArtMetrics(textureKey)
    local metrics = addon.NewUIAssets and addon.NewUIAssets.Metrics or nil
    return metrics and metrics[textureKey] or nil
end

local function resolveHomeInsets(insetKey, fallbackInsets)
    local insets = addon.NewUIAssets and addon.NewUIAssets.Insets or nil
    return insets and insets[insetKey] or fallbackInsets or {}
end

local function resolveHomeAsset(textureKey, fallbackInsets, fallbackWidth, fallbackHeight)
    local metrics = resolveHomeArtMetrics(textureKey) or {}
    return {
        texture = resolveHomeTexturePath(textureKey),
        sourceWidth = tonumber(metrics.sourceWidth) or tonumber(fallbackWidth) or 1,
        sourceHeight = tonumber(metrics.sourceHeight) or tonumber(fallbackHeight) or 1,
        insets = metrics.insets or fallbackInsets or {},
        texCoord = metrics.texCoord,
    }
end

local function CreateArtFrame(parent, rect, asset)
    local frame = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    ApplyRect(frame, parent, rect)
    local hasAuthoredArt = type(asset) == "table" and type(asset.texture) == "string" and asset.texture ~= ""
    CreateBackdrop(frame, hasAuthoredArt)

    local texture = frame:CreateTexture(nil, hasAuthoredArt and "ARTWORK" or "BACKGROUND")
    texture:SetAllPoints(frame)
    if hasAuthoredArt then
        texture:SetTexture(asset.texture)
        texture:SetVertexColor(1, 1, 1, 1)
        if type(asset.texCoord) == "table" and #asset.texCoord == 4 then
            texture:SetTexCoord(asset.texCoord[1], asset.texCoord[3], asset.texCoord[2], asset.texCoord[4])
        end
    else
        texture:SetColorTexture(0.12, 0.12, 0.14, 0.98)
    end
    frame.Texture = texture

    local content = CreateArtContentFrame(frame, nil, rect.w, rect.h, asset and asset.insets or nil)
    frame.Content = content
    return frame
end

CreateArtContentFrame = function(parent, textureKey, width, height, fallbackInsets)
    local metrics = resolveHomeArtMetrics(textureKey)
    local sourceWidth = metrics and tonumber(metrics.sourceWidth) or tonumber(width) or 0
    local sourceHeight = metrics and tonumber(metrics.sourceHeight) or tonumber(height) or 0
    local metricInsets = metrics and metrics.insets or fallbackInsets or {}
    local scaleX = sourceWidth > 0 and (tonumber(width) or 0) / sourceWidth or 1
    local scaleY = sourceHeight > 0 and (tonumber(height) or 0) / sourceHeight or 1
    local insetLeft = math.floor(((tonumber(metricInsets.left) or 0) * scaleX) + 0.5)
    local insetRight = math.floor(((tonumber(metricInsets.right) or 0) * scaleX) + 0.5)
    local insetTop = math.floor(((tonumber(metricInsets.top) or 0) * scaleY) + 0.5)
    local insetBottom = math.floor(((tonumber(metricInsets.bottom) or 0) * scaleY) + 0.5)
    local contentX = insetLeft
    local contentY = -insetTop
    local contentW = math.max(1, (tonumber(width) or 0) - insetLeft - insetRight)
    local contentH = math.max(1, (tonumber(height) or 0) - insetTop - insetBottom)
    local content = CreateFrame("Frame", nil, parent)
    content:SetPoint("TOPLEFT", parent, "TOPLEFT", contentX, contentY)
    content:SetSize(contentW, contentH)
    content.Insets = {
        left = insetLeft,
        right = insetRight,
        top = insetTop,
        bottom = insetBottom,
    }
    return content
end

local function ApplyHomeArtTexture(frame, textureKey, layer, fallbackColor)
    local texturePath = resolveHomeTexturePath(textureKey)
    local metrics = resolveHomeArtMetrics(textureKey)
    local texture = frame:CreateTexture(nil, layer or "BACKGROUND")
    texture:SetAllPoints(frame)
    if texturePath then
        texture:SetTexture(texturePath)
        texture:SetVertexColor(1, 1, 1, 0.98)
        if metrics and type(metrics.texCoord) == "table" and #metrics.texCoord == 4 then
            texture:SetTexCoord(metrics.texCoord[1], metrics.texCoord[3], metrics.texCoord[2], metrics.texCoord[4])
        end
    else
        local color = fallbackColor or { 0.08, 0.08, 0.1, 0.45 }
        texture:SetColorTexture(color[1], color[2], color[3], color[4] or 1)
    end
    return texture
end

local function CreateCardBackdrop(frame, options)
    local cardTheme = theme or cardFallback
    local bgFile = resolveHomeTexturePath(options and options.backgroundTextureKey)
    local edgeFile = resolveHomeTexturePath(options and options.borderTextureKey)
    local backgroundColor = bgFile and { 0.10, 0.08, 0.05, 0.92 } or (cardTheme.Background or cardFallback.Background)

    if frame.SetBackdrop then
        frame:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = edgeFile or "Interface\\Buttons\\WHITE8X8",
            edgeSize = 1,
            insets = {
                left = 1,
                right = 1,
                top = 1,
                bottom = 1,
            },
        })
        frame:SetBackdropColor(unpack(backgroundColor))
        frame:SetBackdropBorderColor(unpack(cardTheme.Border or cardFallback.Border))
    elseif not bgFile then
        local background = frame:CreateTexture(nil, "BACKGROUND")
        background:SetAllPoints(frame)
        background:SetColorTexture(unpack(backgroundColor))
        frame.background = background
    end

    if bgFile then
        local backgroundArt = frame:CreateTexture(nil, "BACKGROUND")
        backgroundArt:SetAllPoints(frame)
        backgroundArt:SetTexture(bgFile)
        backgroundArt:SetVertexColor(1, 1, 1, 0.98)
        frame.BackgroundArt = backgroundArt
    end
end

local function CreateBadge(parent, size, label, fillColor, textColor)
    local badge = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    badge:SetSize(size, size)

    if badge.SetBackdrop then
        badge:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Buttons\\WHITE8X8",
            edgeSize = 1,
            insets = { left = 1, right = 1, top = 1, bottom = 1 },
        })
        badge:SetBackdropColor(fillColor[1], fillColor[2], fillColor[3], fillColor[4] or 1)
        badge:SetBackdropBorderColor(0.48, 0.38, 0.18, 1)
    else
        local background = badge:CreateTexture(nil, "BACKGROUND")
        background:SetAllPoints(badge)
        background:SetColorTexture(fillColor[1], fillColor[2], fillColor[3], fillColor[4] or 1)
        badge.background = background
    end

    local text = badge:CreateFontString(nil, "OVERLAY", size >= 24 and "GameFontNormalLarge" or "GameFontNormal")
    text:SetPoint("CENTER", badge, "CENTER", 0, 0)
    text:SetText(label)
    text:SetTextColor((textColor or cardFallback.Title)[1], (textColor or cardFallback.Title)[2], (textColor or cardFallback.Title)[3], (textColor or cardFallback.Title)[4] or 1)
    badge.Text = text

    return badge
end

local function GetCardBadgeInfo(titleText)
    local normalized = string.lower(trimString(titleText))

    if string.find(normalized, "raid", 1, true) then
        return "R", { 0.46, 0.18, 0.14, 0.92 }, { 0.96, 0.82, 0.52, 1 }
    end

    if string.find(normalized, "members", 1, true) then
        return "M", { 0.12, 0.34, 0.18, 0.92 }, { 0.56, 0.94, 0.42, 1 }
    end

    if string.find(normalized, "mythic", 1, true) then
        return "+", { 0.24, 0.16, 0.34, 0.92 }, { 0.84, 0.62, 1, 1 }
    end

    return "N", { 0.14, 0.24, 0.34, 0.92 }, { 0.38, 0.74, 1, 1 }
end

local function GetStatusVisual(statusText)
    local normalized = string.lower(trimString(statusText))

    if string.find(normalized, "ready", 1, true) then
        return "OK", { 0.36, 0.86, 0.42, 1 }
    end

    if normalized == "online" then
        return "ON", { 0.36, 0.74, 1, 1 }
    end

    if normalized == "" or string.find(normalized, "no readiness", 1, true) then
        return "--", { 0.72, 0.7, 0.62, 1 }
    end

    return "!", { 0.94, 0.68, 0.2, 1 }
end

local function OpenLegacyMainFrameView(viewKey)
    local resolvedView = trimString(viewKey)

    if resolvedView == "" or not addon.Router or type(addon.Router.Navigate) ~= "function" or not addon.MainFrame or type(addon.MainFrame.ShowForCurrentView) ~= "function" then
        return
    end

    addon.Router:Navigate(resolvedView)
    addon.MainFrame:ShowForCurrentView()
end

local function GetRosterRowRank(member)
    if type(member) ~= "table" then
        return -1
    end

    local normalizedStatus = string.lower(trimString(type(member.readiness) == "table" and member.readiness.statusText or ""))
    local statusRank = 0

    if string.find(normalizedStatus, "ready", 1, true) then
        statusRank = 3
    elseif normalizedStatus == "online" then
        statusRank = 2
    elseif normalizedStatus ~= "" and normalizedStatus ~= "no readiness data" then
        statusRank = 1
    end

    return ((member.online and 1 or 0) * 1000000)
        + (math.min(math.max(tonumber(member.itemLevel) or 0, 0), 9999) * 100)
        + statusRank
end

local function findHomeUnitIdByFullName(fullName)
    local realmName = GetRealmName and GetRealmName() or "Unknown"
    local normalized = addon.Store and addon.Store.NormalizeFullName and addon.Store:NormalizeFullName(fullName, realmName) or trimString(fullName)

    if trimString(normalized) == "" then
        return nil
    end

    local function matchesUnit(unitId)
        if not unitId or (type(UnitExists) == "function" and not UnitExists(unitId)) then
            return false
        end

        local unitName, unitRealm = UnitFullName and UnitFullName(unitId)
        local resolvedFullName = addon.Store and addon.Store.NormalizeFullName and addon.Store:NormalizeFullName(unitName, trimString(unitRealm) ~= "" and unitRealm or realmName) or trimString(unitName)
        return resolvedFullName == normalized
    end

    if matchesUnit("player") then
        return "player"
    end

    local subgroupCount = GetNumSubgroupMembers and GetNumSubgroupMembers() or 0
    for index = 1, subgroupCount do
        local unitId = "party" .. index
        if matchesUnit(unitId) then
            return unitId
        end
    end

    local raidCount = GetNumGroupMembers and GetNumGroupMembers() or 0
    for index = 1, raidCount do
        local unitId = "raid" .. index
        if matchesUnit(unitId) then
            return unitId
        end
    end

    return nil
end

local function EnsureHomeAvatar(parent)
    if parent.Avatar then
        return parent.Avatar
    end

    local avatar = CreateFrame("Frame", nil, parent, BackdropTemplateMixin and "BackdropTemplate" or nil)
    avatar:SetSize(20, 20)
    if avatar.SetBackdrop then
        avatar:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Buttons\\WHITE8X8",
            edgeSize = 1,
            insets = { left = 1, right = 1, top = 1, bottom = 1 },
        })
        avatar:SetBackdropColor(0.02, 0.03, 0.04, 0.88)
        avatar:SetBackdropBorderColor(0.18, 0.16, 0.12, 0.72)
    end

    local portrait = avatar:CreateTexture(nil, "ARTWORK")
    portrait:SetPoint("TOPLEFT", avatar, "TOPLEFT", 1, -1)
    portrait:SetPoint("BOTTOMRIGHT", avatar, "BOTTOMRIGHT", -1, 1)
    portrait:SetTexture("Interface\\Buttons\\WHITE8X8")
    portrait:SetTexCoord(0.07, 0.93, 0.07, 0.93)

    local fallbackText = avatar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    fallbackText:SetAllPoints(avatar)
    fallbackText:SetJustifyH("CENTER")
    fallbackText:SetJustifyV("MIDDLE")

    avatar.Portrait = portrait
    avatar.FallbackText = fallbackText
    parent.Avatar = avatar
    return avatar
end

local function normalizeHomeClassToken(classFile)
    local normalized = string.upper((trimString(classFile):gsub("%s+", "")))
    if normalized ~= "" and type(CLASS_ICON_TCOORDS) == "table" and CLASS_ICON_TCOORDS[normalized] then
        return normalized
    end

    for _, lookup in ipairs({ LOCALIZED_CLASS_NAMES_MALE or {}, LOCALIZED_CLASS_NAMES_FEMALE or {} }) do
        for token, localized in pairs(lookup) do
            if string.upper((trimString(localized):gsub("%s+", ""))) == normalized then
                return token
            end
        end
    end

    return normalized
end

local function SetHomeAvatar(avatar, fullName, classFile, preferClassIcon)
    if not avatar then
        return
    end

    local resolvedClassFile = normalizeHomeClassToken(classFile)
    local unitId = findHomeUnitIdByFullName(fullName)

    if preferClassIcon and resolvedClassFile ~= "" and type(CLASS_ICON_TCOORDS) == "table" and CLASS_ICON_TCOORDS[resolvedClassFile] then
        local coords = CLASS_ICON_TCOORDS[resolvedClassFile]
        avatar.Portrait:SetTexture(CLASS_ICON_TEXTURE)
        avatar.Portrait:SetTexCoord(coords[1], coords[2], coords[3], coords[4])
        avatar.Portrait:SetVertexColor(1, 1, 1, 0.92)
        avatar.FallbackText:SetText("")
        return
    end

    if unitId and type(SetPortraitTexture) == "function" then
        SetPortraitTexture(avatar.Portrait, unitId)
        avatar.Portrait:SetTexCoord(0.07, 0.93, 0.07, 0.93)
        avatar.Portrait:SetVertexColor(1, 1, 1, 0.95)
        avatar.FallbackText:SetText("")
        return
    end

    if resolvedClassFile ~= "" and type(CLASS_ICON_TCOORDS) == "table" and CLASS_ICON_TCOORDS[resolvedClassFile] then
        local coords = CLASS_ICON_TCOORDS[resolvedClassFile]
        avatar.Portrait:SetTexture(CLASS_ICON_TEXTURE)
        avatar.Portrait:SetTexCoord(coords[1], coords[2], coords[3], coords[4])
        avatar.Portrait:SetVertexColor(1, 1, 1, 0.92)
        avatar.FallbackText:SetText("")
        return
    end

    avatar.Portrait:SetTexture("Interface\\Buttons\\WHITE8X8")
    avatar.Portrait:SetTexCoord(0, 1, 0, 1)
    avatar.Portrait:SetVertexColor(0.26, 0.22, 0.14, 0.55)
    avatar.FallbackText:SetText(string.upper(string.sub(trimString(fullName) ~= "" and trimString(fullName) or "?", 1, 1)))
end

local function CreateCard(parent, rect, titleText, valueText, options)
    local card = CreateArtFrame(parent, rect, resolveHomeAsset(options and options.backgroundTextureKey, resolveHomeInsets("HomeCard", { left = 20, right = 20, top = 10, bottom = 10 }), rect.w, rect.h))
    local content = card.Content
    local iconPath = resolveHomeTexturePath(options and options.iconTextureKey)
    local contentWidth = tonumber(content:GetWidth()) or math.max(1, rect.w)
    local contentHeight = tonumber(content:GetHeight()) or rect.h or 84
    local body = CreateFrame("Frame", nil, card)
    body:SetAllPoints(content)

    local badgeText, badgeFill, valueColor = GetCardBadgeInfo(titleText)
    local badge = nil
    if iconPath then
        local icon = card:CreateTexture(nil, "OVERLAY")
        local iconSize = math.max(46, math.floor(contentHeight * 0.46))
        icon:SetSize(iconSize, iconSize)
        icon:SetPoint("LEFT", content, "LEFT", 8, -2)
        icon:SetTexture(iconPath)
        icon:SetVertexColor(1, 1, 1, 0.96)
        card.IconTexture = icon
    else
        badge = CreateBadge(card, math.max(28, math.floor(contentHeight * 0.36)), badgeText, badgeFill, valueColor)
        badge:SetPoint("LEFT", content, "LEFT", 8, -2)
    end

    local title = card:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    title:SetPoint("CENTER", body, "CENTER", 0, 12)
    title:SetWidth(math.max(1, contentWidth))
    title:SetJustifyH("CENTER")
    title:SetWordWrap(false)
    title:SetText(string.upper(titleText))
    title:SetTextColor(valueColor[1], valueColor[2], valueColor[3], valueColor[4] or 1)
    title:SetScale(0.92)

    local value = card:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    value:SetPoint("CENTER", body, "CENTER", 0, -12)
    value:SetWidth(math.max(1, contentWidth))
    value:SetJustifyH("CENTER")
    value:SetWordWrap(false)
    value:SetText(valueText)
    value:SetTextColor(valueColor[1], valueColor[2], valueColor[3], valueColor[4] or 1)
    value:SetScale(0.92)

    card.Content = content
    card.Body = body
    card.Badge = badge
    card.TitleText = title
    card.ValueText = value

    return card
end

local function CreatePanel(parent, rect, titleText, options)
    local panel = CreateArtFrame(parent, rect, resolveHomeAsset(options and options.backgroundTextureKey, resolveHomeInsets("HomePanel", resolveHomeInsets("Panel", { left = 24, right = 24, top = 48, bottom = 20 })), rect.w, rect.h))
    local headerFile = resolveHomeTexturePath("HomePanelHeader")
    local footerFile = resolveHomeTexturePath("HomeFooterBackground")
    local headerMetrics = resolveHomeArtMetrics("HomePanelHeader") or {}
    local footerMetrics = resolveHomeArtMetrics("HomeFooterBackground") or {}
    local safeContent = panel.Content
    local headerHeight = headerFile and 40 or 28
    local footerHeight = footerFile and 32 or 18
    local bodyTopOffset = headerFile and 30 or 20

    if panel.Texture and panel.Texture.SetDrawLayer then
        panel.Texture:SetDrawLayer("BACKGROUND", 1)
        panel.Texture:SetVertexColor(1, 1, 1, 1)
    end

    local headerFill = panel:CreateTexture(nil, "ARTWORK")
    headerFill:SetDrawLayer("ARTWORK", 2)
    headerFill:SetPoint("TOPLEFT", panel, "TOPLEFT", 1, -1)
    headerFill:SetPoint("TOPRIGHT", panel, "TOPRIGHT", -1, -1)
    headerFill:SetHeight(headerHeight)
    if headerFile then
        headerFill:SetTexture(headerFile)
        headerFill:SetVertexColor(1, 1, 1, 0.96)
        if type(headerMetrics.texCoord) == "table" and #headerMetrics.texCoord == 4 then
            headerFill:SetTexCoord(headerMetrics.texCoord[1], headerMetrics.texCoord[3], headerMetrics.texCoord[2], headerMetrics.texCoord[4])
        end
    else
        headerFill:SetColorTexture(0.09, 0.08, 0.06, 0.82)
    end

    local title = panel:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
    title:SetPoint("CENTER", headerFill, "CENTER", 0, 0)
    title:SetWidth(math.max(1, rect.w - 48))
    title:SetJustifyH("CENTER")
    title:SetText(titleText)
    title:SetTextColor(unpack(panelTheme.Title or panelFallback.Title))

    local footerFill = panel:CreateTexture(nil, "ARTWORK")
    footerFill:SetDrawLayer("ARTWORK", 2)
    footerFill:SetPoint("BOTTOMLEFT", panel, "BOTTOMLEFT", 1, 1)
    footerFill:SetPoint("BOTTOMRIGHT", panel, "BOTTOMRIGHT", -1, 1)
    footerFill:SetHeight(footerHeight)
    if footerFile then
        footerFill:SetTexture(footerFile)
        footerFill:SetVertexColor(1, 1, 1, 0.96)
        if type(footerMetrics.texCoord) == "table" and #footerMetrics.texCoord == 4 then
            footerFill:SetTexCoord(footerMetrics.texCoord[1], footerMetrics.texCoord[3], footerMetrics.texCoord[2], footerMetrics.texCoord[4])
        end
    else
        footerFill:SetColorTexture(0.09, 0.08, 0.06, 0.82)
    end

    local footerLabel = panel:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    footerLabel:SetPoint("LEFT", footerFill, "LEFT", 16, 4)
    footerLabel:SetText(trimString(options and options.footerLabel) ~= "" and options.footerLabel or "Overview")
    footerLabel:SetTextColor(0.92, 0.84, 0.62, 1)

    local footerLink = panel:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    footerLink:SetPoint("RIGHT", footerFill, "RIGHT", -16, 4)
    footerLink:SetText(trimString(options and options.linkText) ~= "" and options.linkText or "View")
    footerLink:SetTextColor(0.36, 0.74, 1, 1)

    if trimString(options and options.linkText) ~= "" and type(options and options.onLinkClick) == "function" then
        local footerLinkButton = CreateFrame("Button", nil, panel)
        footerLinkButton:SetFrameLevel(panel:GetFrameLevel() + 8)
        footerLinkButton:SetPoint("TOPLEFT", footerLink, "TOPLEFT", -4, 2)
        footerLinkButton:SetPoint("BOTTOMRIGHT", footerLink, "BOTTOMRIGHT", 8, -2)
        footerLinkButton:RegisterForClicks("LeftButtonUp")
        footerLinkButton:SetScript("OnEnter", function()
            footerLink:SetTextColor(0.58, 0.84, 1, 1)
        end)
        footerLinkButton:SetScript("OnLeave", function()
            footerLink:SetTextColor(0.36, 0.74, 1, 1)
        end)
        footerLinkButton:SetScript("OnClick", function()
            options.onLinkClick(panel)
        end)
        panel.FooterLinkButton = footerLinkButton
    end

    local content = CreateFrame("Frame", nil, panel)
    content:SetPoint("TOPLEFT", safeContent, "TOPLEFT", 0, -bodyTopOffset)
    content:SetPoint("BOTTOMRIGHT", safeContent, "BOTTOMRIGHT", 0, footerHeight + 2)

    panel.SafeContent = safeContent
    panel.HeaderFill = headerFill
    panel.FooterFill = footerFill
    panel.FooterLabel = footerLabel
    panel.FooterLink = footerLink
    panel.TitleText = title
    panel.Content = content

    return panel
end

local function CreatePanelHeaderRow(parent)
    local row = CreateFrame("Frame", nil, parent)
    row:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, 0)
    row:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, 0)
    row:SetHeight(20)

    local rowWidth = tonumber(parent:GetWidth()) or 0
    local background = ApplyHomeArtTexture(row, "HomeRowBackground", "BACKGROUND", { 0.09, 0.08, 0.06, 0.52 })
    local content = CreateArtContentFrame(row, "HomeRowBackground", rowWidth, 20, { left = 8, right = 8, top = 4, bottom = 4 })

    local player = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    player:SetPoint("LEFT", content, "LEFT", 28, 0)
    player:SetWidth(170)
    player:SetJustifyH("LEFT")
    player:SetText("Player")
    player:SetTextColor(0.92, 0.84, 0.62, 1)

    local role = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    role:SetPoint("LEFT", content, "LEFT", 198, 0)
    role:SetWidth(76)
    role:SetJustifyH("LEFT")
    role:SetText("Role")
    role:SetTextColor(0.92, 0.84, 0.62, 1)

    local ilvl = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    ilvl:SetPoint("LEFT", content, "LEFT", 312, 0)
    ilvl:SetWidth(44)
    ilvl:SetJustifyH("RIGHT")
    ilvl:SetText("iLvl")
    ilvl:SetTextColor(0.92, 0.84, 0.62, 1)

    local status = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    status:SetPoint("RIGHT", content, "RIGHT", 0, 0)
    status:SetWidth(104)
    status:SetJustifyH("RIGHT")
    status:SetText("Status")
    status:SetTextColor(0.92, 0.84, 0.62, 1)

    local column1 = row:CreateTexture(nil, "ARTWORK")
    column1:SetPoint("TOPLEFT", content, "TOPLEFT", 184, 2)
    column1:SetPoint("BOTTOMLEFT", content, "BOTTOMLEFT", 184, -2)
    column1:SetWidth(1)
    column1:SetColorTexture(0.44, 0.34, 0.16, 0.45)

    local column2 = row:CreateTexture(nil, "ARTWORK")
    column2:SetPoint("TOPLEFT", content, "TOPLEFT", 300, 2)
    column2:SetPoint("BOTTOMLEFT", content, "BOTTOMLEFT", 300, -2)
    column2:SetWidth(1)
    column2:SetColorTexture(0.44, 0.34, 0.16, 0.45)

    local column3 = row:CreateTexture(nil, "ARTWORK")
    column3:SetPoint("TOPLEFT", content, "TOPLEFT", 366, 2)
    column3:SetPoint("BOTTOMLEFT", content, "BOTTOMLEFT", 366, -2)
    column3:SetWidth(1)
    column3:SetColorTexture(0.44, 0.34, 0.16, 0.45)

    local divider = row:CreateTexture(nil, "BACKGROUND")
    divider:SetPoint("BOTTOMLEFT", content, "BOTTOMLEFT", 0, -1)
    divider:SetPoint("BOTTOMRIGHT", content, "BOTTOMRIGHT", 0, -1)
    divider:SetHeight(1)
    divider:SetColorTexture(0.44, 0.34, 0.16, 0.8)

    row.Content = content
    row.BackgroundArt = background
    row.PlayerText = player
    row.RoleText = role
    row.ItemLevelText = ilvl
    row.StatusText = status

    return row
end

local function CreateRosterRow(parent, offsetY, playerName, roleText, itemLevelText, statusText, fullName, classFile)
    local row = CreateFrame("Frame", nil, parent)
    row:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, offsetY)
    row:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, offsetY)
    row:SetHeight(24)

    local statusMarkerText, statusColor = GetStatusVisual(statusText)
    local rowWidth = tonumber(parent:GetWidth()) or 0
    local background = ApplyHomeArtTexture(row, "HomeRowBackground", "BACKGROUND", { 0.08, 0.08, 0.1, 0.52 })
    local content = CreateArtContentFrame(row, "HomeRowBackground", rowWidth, 24, { left = 8, right = 8, top = 4, bottom = 4 })

    local topGlow = row:CreateTexture(nil, "ARTWORK")
    topGlow:SetPoint("TOPLEFT", row, "TOPLEFT", 0, 0)
    topGlow:SetPoint("TOPRIGHT", row, "TOPRIGHT", 0, 0)
    topGlow:SetHeight(1)
    topGlow:SetColorTexture(0.58, 0.44, 0.16, 0.18)

    local avatar = EnsureHomeAvatar(row)
    avatar:ClearAllPoints()
    avatar:SetPoint("LEFT", content, "LEFT", 0, 0)
    avatar:SetSize(20, 20)
    SetHomeAvatar(avatar, fullName or playerName, classFile, true)

    local normalizedRole = string.lower(trimString(roleText))
    local roleIconPath = nil
    if string.find(normalizedRole, "tank", 1, true) then
        roleIconPath = resolveHomeTexturePath("Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Role_Tank-l.png")
    elseif string.find(normalizedRole, "heal", 1, true) then
        roleIconPath = resolveHomeTexturePath("Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Role_Healer-l.png")
    elseif normalizedRole ~= "" and normalizedRole ~= "-" then
        roleIconPath = resolveHomeTexturePath("Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Role_DPS-l.png")
    end

    local normalizedStatus = string.lower(trimString(statusText))
    local statusIconPath = resolveHomeTexturePath("Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Warning-l.png")
    if string.find(normalizedStatus, "ready", 1, true) then
        statusIconPath = resolveHomeTexturePath("Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Ready-l.png")
    elseif normalizedStatus == "online" then
        statusIconPath = resolveHomeTexturePath("HomeMembersCardIcon")
    elseif string.find(normalizedStatus, "gem", 1, true) then
        statusIconPath = resolveHomeTexturePath("Interface\\AddOns\\GH3\\Media\\Icons\\Icon_NeedGems-l.png")
    end

    local player = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    player:SetPoint("LEFT", avatar, "RIGHT", 8, 0)
    player:SetWidth(150)
    player:SetWordWrap(false)
    player:SetJustifyH("LEFT")
    player:SetText(playerName)

    local roleIcon = nil
    if roleIconPath then
        roleIcon = row:CreateTexture(nil, "OVERLAY")
        roleIcon:SetSize(22, 22)
        roleIcon:SetPoint("LEFT", content, "LEFT", 208, 0)
        roleIcon:SetTexture(roleIconPath)
        roleIcon:SetVertexColor(1, 1, 1, 0.98)
    end

    local role = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    role:SetPoint("LEFT", content, "LEFT", 232, 0)
    role:SetWidth(48)
    role:SetWordWrap(false)
    role:SetJustifyH("LEFT")
    role:SetText(roleIconPath and "" or roleText)
    role:SetTextColor(0.76, 0.72, 0.62, 1)

    local ilvl = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    ilvl:SetPoint("LEFT", content, "LEFT", 306, 0)
    ilvl:SetWidth(44)
    ilvl:SetWordWrap(false)
    ilvl:SetJustifyH("RIGHT")
    ilvl:SetText(itemLevelText)
    ilvl:SetTextColor(0.95, 0.90, 0.70, 1)

    local statusIcon = row:CreateTexture(nil, "OVERLAY")
    statusIcon:SetSize(20, 20)
    statusIcon:SetPoint("RIGHT", content, "RIGHT", -92, 0)
    statusIcon:SetTexture(statusIconPath)
    statusIcon:SetVertexColor(1, 1, 1, 0.98)

    local status = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    status:SetPoint("RIGHT", content, "RIGHT", 0, 0)
    status:SetWidth(82)
    status:SetJustifyH("RIGHT")
    status:SetWordWrap(false)
    status:SetText(statusText)
    status:SetTextColor(statusColor[1], statusColor[2], statusColor[3], statusColor[4] or 1)

    local statusMarker = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    statusMarker:SetPoint("RIGHT", statusIcon, "LEFT", -4, 0)
    statusMarker:SetText("")

    local column1 = row:CreateTexture(nil, "ARTWORK")
    column1:SetPoint("TOPLEFT", content, "TOPLEFT", 178, 2)
    column1:SetPoint("BOTTOMLEFT", content, "BOTTOMLEFT", 178, -2)
    column1:SetWidth(1)
    column1:SetColorTexture(0.44, 0.34, 0.16, 0.18)

    local column2 = row:CreateTexture(nil, "ARTWORK")
    column2:SetPoint("TOPLEFT", content, "TOPLEFT", 294, 2)
    column2:SetPoint("BOTTOMLEFT", content, "BOTTOMLEFT", 294, -2)
    column2:SetWidth(1)
    column2:SetColorTexture(0.44, 0.34, 0.16, 0.18)

    local column3 = row:CreateTexture(nil, "ARTWORK")
    column3:SetPoint("TOPLEFT", content, "TOPLEFT", 360, 2)
    column3:SetPoint("BOTTOMLEFT", content, "BOTTOMLEFT", 360, -2)
    column3:SetWidth(1)
    column3:SetColorTexture(0.44, 0.34, 0.16, 0.18)

    local divider = row:CreateTexture(nil, "ARTWORK")
    divider:SetPoint("BOTTOMLEFT", content, "BOTTOMLEFT", 0, 0)
    divider:SetPoint("BOTTOMRIGHT", content, "BOTTOMRIGHT", 0, 0)
    divider:SetHeight(1)
    divider:SetColorTexture(0.4, 0.32, 0.16, 0.45)

    row.Content = content
    row.BackgroundArt = background
    row.Avatar = avatar
    row.RoleIcon = roleIcon
    row.PlayerText = player
    row.RoleText = role
    row.ItemLevelText = ilvl
    row.StatusIcon = statusIcon
    row.StatusMarker = statusMarker
    row.StatusText = status

    return row
end

local function CreateEventRow(parent, offsetY, eventText, timeText)
    local row = CreateFrame("Frame", nil, parent)
    row:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, offsetY)
    row:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, offsetY)
    row:SetHeight(24)

    local rowWidth = tonumber(parent:GetWidth()) or 0
    local background = ApplyHomeArtTexture(row, "HomeRowBackground", "BACKGROUND", { 0.08, 0.08, 0.1, 0.42 })
    local content = CreateArtContentFrame(row, "HomeRowBackground", rowWidth, 24, { left = 8, right = 8, top = 4, bottom = 4 })

    local normalizedEvent = string.lower(trimString(eventText))
    local eventIconPath = resolveHomeTexturePath("Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Raid-l.png")
    if string.find(normalizedEvent, "mythic", 1, true) then
        eventIconPath = resolveHomeTexturePath("Interface\\AddOns\\GH3\\Media\\Icons\\Event_Mythic.png")
    elseif string.find(normalizedEvent, "achievement", 1, true) then
        eventIconPath = resolveHomeTexturePath("Interface\\AddOns\\GH3\\Media\\Icons\\Event_Achievement.png")
    elseif string.find(normalizedEvent, "alt", 1, true) then
        eventIconPath = resolveHomeTexturePath("Interface\\AddOns\\GH3\\Media\\Icons\\Icon_Raid-l.png")
    end

    local badge = row:CreateTexture(nil, "OVERLAY")
    badge:SetSize(24, 24)
    badge:SetPoint("LEFT", content, "LEFT", 0, 0)
    badge:SetTexture(eventIconPath)
    badge:SetVertexColor(1, 1, 1, 0.98)

    local event = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    event:SetPoint("LEFT", content, "LEFT", 32, 0)
    event:SetWidth(250)
    event:SetWordWrap(false)
    event:SetJustifyH("LEFT")
    event:SetText(eventText)

    local time = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    time:SetPoint("RIGHT", content, "RIGHT", 0, 0)
    time:SetWidth(90)
    time:SetJustifyH("RIGHT")
    time:SetWordWrap(false)
    time:SetText(timeText)
    time:SetTextColor(0.36, 0.74, 1, 1)

    local divider = row:CreateTexture(nil, "ARTWORK")
    divider:SetPoint("BOTTOMLEFT", content, "BOTTOMLEFT", 0, 0)
    divider:SetPoint("BOTTOMRIGHT", content, "BOTTOMRIGHT", 0, 0)
    divider:SetHeight(1)
    divider:SetColorTexture(0.4, 0.32, 0.16, 0.35)

    row.Content = content
    row.BackgroundArt = background
    row.Badge = badge
    row.EventText = event
    row.TimeText = time

    return row
end

local function CreateHighlightRow(parent, offsetY, leftText, rightText)
    local row = CreateFrame("Frame", nil, parent)
    row:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, offsetY)
    row:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, offsetY)
    row:SetHeight(24)

    local rowWidth = tonumber(parent:GetWidth()) or 0
    local background = ApplyHomeArtTexture(row, "HomeRowBackground", "BACKGROUND", { 0.08, 0.08, 0.1, 0.18 })
    local content = CreateArtContentFrame(row, "HomeRowBackground", rowWidth, 24, { left = 8, right = 8, top = 4, bottom = 4 })

    local left = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    left:SetPoint("LEFT", content, "LEFT", 0, 0)
    left:SetText(leftText)

    local right = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    right:SetPoint("RIGHT", content, "RIGHT", 0, 0)
    right:SetText(rightText)

    row.Content = content
    row.BackgroundArt = background
    row.LeftText = left
    row.RightText = right

    return row
end

local function CreateHighlightColumnRow(parent, offsetY, values, widths, fontObject)
    local row = CreateFrame("Frame", nil, parent)
    row:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, offsetY)
    row:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, offsetY)
    row:SetHeight(18)

    local rowWidth = tonumber(parent:GetWidth()) or 0
    local background = ApplyHomeArtTexture(row, "HomeRowBackground", "BACKGROUND", { 0.08, 0.08, 0.1, fontObject == "GameFontNormalSmall" and 0.35 or 0.18 })
    local content = CreateArtContentFrame(row, "HomeRowBackground", rowWidth, 18, { left = 8, right = 8, top = 4, bottom = 4 })

    local leftOffset = 0

    for index, value in ipairs(values or {}) do
        local cell = row:CreateFontString(nil, "OVERLAY", fontObject or "GameFontHighlightSmall")
        cell:SetPoint("LEFT", content, "LEFT", leftOffset, 0)
        cell:SetWidth(widths and widths[index] or 0)
        cell:SetWordWrap(false)
        cell:SetJustifyH("LEFT")
        cell:SetText(value)

        row["Cell" .. index] = cell
        leftOffset = leftOffset + (widths and widths[index] or 0)
    end

    row.Content = content
    row.BackgroundArt = background
    return row
end

local function CreateLootLine(parent, offsetY, lineTextOrRow)
    local row = CreateFrame("Frame", nil, parent)
    row:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, offsetY)
    row:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, offsetY)
    row:SetHeight(22)

    local rowWidth = tonumber(parent:GetWidth()) or 0
    local background = ApplyHomeArtTexture(row, "HomeRowBackground", "BACKGROUND", { 0.08, 0.08, 0.1, 0.4 })
    local content = CreateArtContentFrame(row, "HomeRowBackground", rowWidth, 22, { left = 8, right = 8, top = 4, bottom = 4 })

    local function attachItemHyperlinkTooltip(widget, itemLink, fallbackTitle)
        if not widget then
            return
        end

        widget:EnableMouse(true)
        widget:SetScript("OnEnter", function(control)
            local link = trimString(itemLink)

            GameTooltip:SetOwner(control, "ANCHOR_RIGHT")

            if link ~= "" and GameTooltip.SetHyperlink then
                GameTooltip:SetHyperlink(link)
            else
                GameTooltip:SetText(trimString(fallbackTitle) ~= "" and fallbackTitle or "Item", 0.93, 0.84, 0.63, 1, true)
            end

            GameTooltip:Show()
        end)
        widget:SetScript("OnLeave", function()
            GameTooltip:Hide()
        end)
    end

    if type(lineTextOrRow) == "table" then
        local playerName = trimString(lineTextOrRow.player)
        local itemName = trimString(lineTextOrRow.itemName)
        local itemLink = trimString(lineTextOrRow.itemLink)
        local itemDisplay = trimString(lineTextOrRow.itemLink) ~= "" and trimString(lineTextOrRow.itemLink) or trimString(lineTextOrRow.itemName)
        local detailText = trimString(lineTextOrRow.metaText) ~= "" and trimString(lineTextOrRow.metaText) or trimString(lineTextOrRow.outcome)

        local badge = CreateBadge(row, 16, string.sub(playerName ~= "" and playerName or "?", 1, 1), { 0.2, 0.16, 0.26, 0.95 }, { 0.9, 0.84, 0.7, 1 })
        badge:SetPoint("LEFT", content, "LEFT", 0, 0)

        local player = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        player:SetPoint("LEFT", content, "LEFT", 22, 0)
        player:SetWidth(200)
        player:SetWordWrap(false)
        player:SetJustifyH("LEFT")
        player:SetText(playerName ~= "" and playerName or "-")
        player:SetTextColor(0.94, 0.76, 0.32, 1)

        local line = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
        line:SetPoint("LEFT", content, "LEFT", 230, 0)
        line:SetPoint("RIGHT", content, "RIGHT", 0, 0)
        line:SetJustifyH("LEFT")
        line:SetWordWrap(false)
        line:SetText(itemDisplay ~= "" and (detailText ~= "" and (itemDisplay .. " | " .. detailText) or itemDisplay) or "-")
        line:SetTextColor(0.95, 0.90, 0.70, 1)

        local itemHotspot = CreateFrame("Button", nil, row)
        itemHotspot:SetPoint("TOPLEFT", line, "TOPLEFT", -2, 2)
        itemHotspot:SetPoint("BOTTOMRIGHT", line, "BOTTOMRIGHT", 2, -2)
        attachItemHyperlinkTooltip(itemHotspot, itemLink, itemName ~= "" and itemName or itemDisplay)

        row.Content = content
        row.BackgroundArt = background
        row.Badge = badge
        row.PlayerText = player
        row.LineText = line
        row.ItemHotspot = itemHotspot
        return row
    end

    local lineText = trimString(lineTextOrRow)
    local playerName, itemName, itemLevel = string.match(lineText, "^(.-)%s*|%s*(.-)%s*|%s*(.-)$")

    if playerName and itemName and itemLevel then
        local badge = CreateBadge(row, 16, string.sub(trimString(playerName) ~= "" and playerName or "?", 1, 1), { 0.2, 0.16, 0.26, 0.95 }, { 0.9, 0.84, 0.7, 1 })
        badge:SetPoint("LEFT", content, "LEFT", 0, 0)

        local player = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        player:SetPoint("LEFT", content, "LEFT", 22, 0)
        player:SetWidth(88)
        player:SetWordWrap(false)
        player:SetJustifyH("LEFT")
        player:SetText(trimString(playerName))
        player:SetTextColor(0.94, 0.76, 0.32, 1)

        local item = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
        item:SetPoint("LEFT", content, "LEFT", 116, 0)
        item:SetWidth(196)
        item:SetWordWrap(false)
        item:SetJustifyH("LEFT")
        item:SetText(trimString(itemName))
        item:SetTextColor(0.72, 0.50, 0.94, 1)

        local level = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        level:SetPoint("RIGHT", content, "RIGHT", 0, 0)
        level:SetText(trimString(itemLevel))
        level:SetTextColor(0.95, 0.90, 0.70, 1)

        row.Content = content
        row.BackgroundArt = background
        row.Badge = badge
        row.PlayerText = player
        row.ItemText = item
        row.LevelText = level
        return row
    end

    local line = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    line:SetPoint("LEFT", content, "LEFT", 0, 0)
    line:SetPoint("RIGHT", content, "RIGHT", 0, 0)
    line:SetJustifyH("LEFT")
    line:SetText(lineText)
    row.Content = content
    row.BackgroundArt = background
    row.LineText = line

    return row
end

local function BuildRecentLootSnapshot()
    local resolved = {
        rows = nil,
    }

    if not addon.BISService or type(addon.BISService.ListLootHistory) ~= "function" then
        return resolved
    end

    local okHistory, history = pcall(addon.BISService.ListLootHistory, addon.BISService)

    if not okHistory or type(history) ~= "table" then
        return resolved
    end

    local rows = {}

    for _, entry in ipairs(history) do
        if #rows >= 4 then
            break
        end

        if type(entry) == "table" then
            local player = trimString(entry.targetId or entry.ownerId or entry.kind or "")
            local itemName = trimString(entry.itemName)
            local itemLevel = tonumber(entry.itemLevel) or 0
            local metaText = trimString(entry.metaText)
            local outcome = trimString(entry.outcome)
            local itemLink = trimString(entry.itemLink)

            if player ~= "" or itemName ~= "" or itemLevel > 0 or metaText ~= "" or outcome ~= "" then
                rows[#rows + 1] = {
                    player = player ~= "" and player or "-",
                    itemName = itemName ~= "" and itemName or "-",
                    itemLink = itemLink,
                    itemLevel = itemLevel > 0 and tostring(itemLevel) or (metaText ~= "" and metaText or (outcome ~= "" and outcome or "-")),
                    metaText = metaText,
                    outcome = outcome,
                    isLive = true,
                    source = "BISService:ListLootHistory",
                }
            end
        end
    end

    resolved.rows = #rows > 0 and rows or nil
    return resolved
end

local function CreateFooterSection(parent, point, relativeTo, relativePoint, x, y, width, labelText, valueText, barColor, barWidthPercent)
    local section = CreateFrame("Frame", nil, parent)
    section:SetPoint(point, relativeTo, relativePoint, x, y)
    section:SetSize(width, 48)
    section:SetFrameLevel(parent:GetFrameLevel() + 2)

    local content = CreateFrame("Frame", nil, section)
    content:SetPoint("TOPLEFT", section, "TOPLEFT", 0, 0)
    content:SetPoint("BOTTOMRIGHT", section, "BOTTOMRIGHT", 0, 0)

    local label = section:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    label:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -4)
    label:SetText(labelText)
    label:SetTextColor(unpack(footerTheme.Title or footerFallback.Title))

    local labelRule = section:CreateTexture(nil, "ARTWORK")
    labelRule:SetPoint("TOPLEFT", label, "BOTTOMLEFT", 0, -2)
    labelRule:SetPoint("TOPRIGHT", content, "TOPRIGHT", 0, -2)
    labelRule:SetHeight(1)
    labelRule:SetColorTexture(0.58, 0.44, 0.16, 0.55)

    local value = section:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    value:SetPoint("TOPLEFT", label, "BOTTOMLEFT", 0, 0)
    value:SetText(valueText)
    value:SetTextColor(unpack(footerTheme.Value or footerFallback.Value))

    local barFrame = CreateFrame("Frame", nil, section)
    barFrame:SetPoint("BOTTOMLEFT", content, "BOTTOMLEFT", 0, 8)
    barFrame:SetPoint("BOTTOMRIGHT", content, "BOTTOMRIGHT", 0, 8)
    barFrame:SetHeight(12)
    barFrame:SetFrameLevel(section:GetFrameLevel() + 4)
    local barBackground = barFrame:CreateTexture(nil, "BACKGROUND")
    barBackground:SetAllPoints(barFrame)
    barBackground:SetColorTexture(0.10, 0.08, 0.05, 0.92)
    local barContent = CreateFrame("Frame", nil, barFrame)
    barContent:SetPoint("TOPLEFT", barFrame, "TOPLEFT", 2, -2)
    barContent:SetPoint("BOTTOMRIGHT", barFrame, "BOTTOMRIGHT", -2, 2)

    local barBorder = barFrame:CreateTexture(nil, "BORDER")
    barBorder:SetPoint("TOPLEFT", barFrame, "TOPLEFT", 0, 0)
    barBorder:SetPoint("BOTTOMRIGHT", barFrame, "BOTTOMRIGHT", 0, 0)
    barBorder:SetColorTexture(0.56, 0.42, 0.16, 0.18)

    local barFill = barFrame:CreateTexture(nil, "OVERLAY")
    barFill:SetPoint("LEFT", barContent, "LEFT", 0, 0)
    barFill:SetPoint("TOP", barContent, "TOP", 0, 0)
    barFill:SetPoint("BOTTOM", barContent, "BOTTOM", 0, 0)
    barFill:SetWidth((tonumber(barContent:GetWidth()) or 0) * (barWidthPercent or 0))
    local fillColor = barColor or footerFallback.FillPrimary
    barFill:SetColorTexture(fillColor[1], fillColor[2], fillColor[3], fillColor[4] or 1)

    section.Content = content
    section.BarFrame = barFrame
    section.LabelText = label
    section.LabelRule = labelRule
    section.ValueText = value
    section.BarBackground = barBackground
    section.BarBorder = barBorder
    section.BarFill = barFill
    section.BarContent = barContent

    return section
end

local function CreateFooter(parent, rect)
    local footer = CreateArtFrame(parent, rect, resolveHomeAsset("HomeFooterBackground", resolveHomeInsets("HomeFooter", { left = 28, right = 28, top = 18, bottom = 14 }), rect.w, rect.h))
    local content = footer.Content
    local sectionHost = CreateFrame("Frame", nil, footer)
    sectionHost:SetPoint("TOPLEFT", footer, "TOPLEFT", 28, -6)
    sectionHost:SetPoint("BOTTOMRIGHT", footer, "BOTTOMRIGHT", -28, 8)

    local topRule = footer:CreateTexture(nil, "ARTWORK")
    topRule:SetPoint("TOPLEFT", footer, "TOPLEFT", 28, -6)
    topRule:SetPoint("TOPRIGHT", footer, "TOPRIGHT", -28, -6)
    topRule:SetHeight(2)
    topRule:SetColorTexture(0.58, 0.44, 0.16, 0.75)

    local footerGap = 14
    local footerWidth = math.max(1, math.floor(tonumber(sectionHost:GetWidth()) or ((tonumber(rect and rect.w) or 1240) - 56)))
    local sectionWidth = math.floor((footerWidth - (footerGap * 2)) / 3)
    local leftWidth = sectionWidth
    local centerX = leftWidth + footerGap
    local centerWidth = sectionWidth
    local rightX = centerX + centerWidth + footerGap
    local rightWidth = math.max(1, footerWidth - rightX)

    footer.LeftSection = CreateFooterSection(sectionHost, "TOPLEFT", sectionHost, "TOPLEFT", 0, 0, leftWidth, "Guild Reputation", "Exalted", footerTheme.FillPrimary or footerFallback.FillPrimary, 0.84)
    footer.CenterSection = CreateFooterSection(sectionHost, "TOPLEFT", sectionHost, "TOPLEFT", centerX, 0, centerWidth, "Guild Renown", "23", footerTheme.FillSecondary or footerFallback.FillSecondary, 0.48)
    footer.RightSection = CreateFooterSection(sectionHost, "TOPLEFT", sectionHost, "TOPLEFT", rightX, 0, rightWidth, "Next Reward", "24", footerTheme.FillTertiary or footerFallback.FillTertiary, 0)

    footer.Content = content
    footer.SectionHost = sectionHost
    footer.TopRule = topRule
    return footer
end

function home:Render(parent, snapshot)
    if not parent then
        return nil
    end

    local homeSnapshot = BuildHomeSnapshot(snapshot)
    local eventsSnapshot = BuildWeeklyEventsSnapshot(snapshot)
    local mythicRaidSnapshot = BuildMPlusRaidHighlightsSnapshot(snapshot)

    local page = CreateFrame("Frame", nil, parent)
    local pageRect = addon.NewUILayout and addon.NewUILayout.Home and addon.NewUILayout.Home.Page or {
        x = 0,
        y = 0,
        w = tonumber(parent:GetWidth()) or 1280,
        h = tonumber(parent:GetHeight()) or 742,
    }
    ApplyRect(page, parent, pageRect)

    page.RaidCard = CreateCard(page, addon.NewUILayout.Home.Cards.Raid, "Upcoming Raid", homeSnapshot.raid.text, { backgroundTextureKey = "HomeRaidCardBackground", borderTextureKey = "HomeCardBorder", iconTextureKey = "HomeRaidCardIcon" })
    page.MembersCard = CreateCard(page, addon.NewUILayout.Home.Cards.Members, "Members Online", homeSnapshot.membersOnline.text, { backgroundTextureKey = "HomeMembersCardBackground", borderTextureKey = "HomeCardBorder", iconTextureKey = "HomeMembersCardIcon" })
    page.MythicCard = CreateCard(page, addon.NewUILayout.Home.Cards.Mythic, "Mythic+ Runs", homeSnapshot.mythicRuns.text, { backgroundTextureKey = "HomeMythicCardBackground", borderTextureKey = "HomeCardBorder", iconTextureKey = "HomeMythicCardIcon" })
    page.NewsCard = CreateCard(page, addon.NewUILayout.Home.Cards.News, "Guild News", homeSnapshot.guildNews.text, { backgroundTextureKey = "HomeNewsCardBackground", borderTextureKey = "HomeCardBorder", iconTextureKey = "HomeNewsCardIcon" })

    page.RosterPanel = CreatePanel(page, addon.NewUILayout.Home.Main.Roster, "Roster & Readiness", { backgroundTextureKey = "HomePanelBackground", borderTextureKey = "HomePanelBorder", footerLabel = "Guild Members", linkText = "View Roster", onLinkClick = function() OpenLegacyMainFrameView("roster") end })
    page.RosterHeader = CreatePanelHeaderRow(page.RosterPanel.Content)
    local rosterRows = {}

    local function buildReadinessText(member)
        local readiness = type(member) == "table" and type(member.readiness) == "table" and member.readiness or nil

        if type(readiness) ~= "table" then
            return member and member.online and "Online" or "No Readiness Data"
        end

        local percent = tonumber(readiness.readinessPercent)
        local statusText = trimString(readiness.statusText)

        if percent ~= nil and statusText ~= "" then
            return string.format("%d%% %s", math.floor(percent + 0.5), statusText)
        end

        if percent ~= nil then
            return string.format("%d%%", math.floor(percent + 0.5))
        end

        if statusText ~= "" then
            return statusText
        end

        return "No Readiness Data"
    end

    if addon.RosterService and type(addon.RosterService.ListCurrentGuildMembersRaw) == "function" then
        local okMembers, members = pcall(addon.RosterService.ListCurrentGuildMembersRaw, addon.RosterService)

        if okMembers and type(members) == "table" then
            table.sort(members, function(left, right)
                local leftRank = GetRosterRowRank(left)
                local rightRank = GetRosterRowRank(right)

                if leftRank == rightRank then
                    return trimString(left and (left.fullName or left.name or left.id)) < trimString(right and (right.fullName or right.name or right.id))
                end

                return leftRank > rightRank
            end)

            for _, member in ipairs(members) do
                if type(member) == "table" then
                    rosterRows[#rosterRows + 1] = {
                        name = trimString(member.name) ~= "" and member.name or trimString(member.fullName or member.id) ~= "" and (member.fullName or member.id) or "Unknown",
                        fullName = trimString(member.fullName or member.id or member.name),
                        classFile = trimString(member.classFile or member.className or member.class),
                        role = trimString(member.primaryRole) ~= "" and member.primaryRole or "-",
                        itemLevel = (tonumber(member.itemLevel) or 0) > 0 and tostring(math.floor(tonumber(member.itemLevel) or 0)) or "-",
                        status = buildReadinessText(member),
                    }
                end

                if #rosterRows >= 5 then
                    break
                end
            end
        end
    end

    if #rosterRows == 0 then
        rosterRows[1] = {
            name = "No guild roster data visible",
            fullName = "",
            classFile = "",
            role = "-",
            itemLevel = "-",
            status = "No Live Data",
        }
    end

    for index = 1, math.min(#rosterRows, 5) do
        local rowOffset = -28 - ((index - 1) * 24)
        page["RosterRow" .. index] = CreateRosterRow(page.RosterPanel.Content, rowOffset, rosterRows[index].name, rosterRows[index].role, rosterRows[index].itemLevel, rosterRows[index].status, rosterRows[index].fullName, rosterRows[index].classFile)
    end

    page.EventsPanel = CreatePanel(page, addon.NewUILayout.Home.Main.Events, "Weekly Events", { backgroundTextureKey = "HomePanelBackground", borderTextureKey = "HomePanelBorder", footerLabel = "Scheduled", linkText = "View Calendar", onLinkClick = function() OpenLegacyMainFrameView("groups") end })
    local weeklyEvents = eventsSnapshot.weeklyEvents or {}
    if #weeklyEvents == 0 then
        weeklyEvents[1] = {
            text = "No scheduled guild events",
            time = "",
            isLive = false,
            source = "empty-state",
        }
    end

    for index = 1, math.min(#weeklyEvents, 4) do
        local eventRow = weeklyEvents[index]
        page["EventRow" .. index] = CreateEventRow(page.EventsPanel.Content, -((index - 1) * 24), eventRow.text, eventRow.time)
    end

    page.HighlightsPanel = CreatePanel(page, addon.NewUILayout.Home.Lower.Highlights, "Mythic+ / Raid Highlights", { backgroundTextureKey = "HomePanelBackground", borderTextureKey = "HomePanelBorder", footerLabel = "Weekly Summary", linkText = "View Leaderboard", onLinkClick = function() OpenLegacyMainFrameView("mythics") end })

    local highlightsContent = page.HighlightsPanel.Content
    local mythicRows = mythicRaidSnapshot.mythicRaidHighlights or {}
    local bestMythic = mythicRows[1]

    local function splitBestKeyText(keyText)
        local resolvedText = trimString(keyText)

        if resolvedText == "" or resolvedText == "No Key" then
            return "", ""
        end

        local dungeonName, keyLevel = resolvedText:match("^(.-)%s*%+(%d+)%s*$")

        if dungeonName and trimString(dungeonName) ~= "" and keyLevel then
            return trimString(dungeonName), keyLevel
        end

        local onlyKeyLevel = resolvedText:match("^%+(%d+)%s*$")

        if onlyKeyLevel then
            return "", onlyKeyLevel
        end

        return resolvedText, ""
    end

    local runsText = page.HighlightsPanel:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    runsText:SetPoint("TOPLEFT", highlightsContent, "TOPLEFT", 0, 20)
    runsText:SetText("Runs Completed: " .. tostring(homeSnapshot.mythicRuns and homeSnapshot.mythicRuns.text or "-"))

    local badgeTexture = page.HighlightsPanel:CreateTexture(nil, "ARTWORK")
    badgeTexture:SetPoint("TOPLEFT", highlightsContent, "TOPLEFT", 10, 8)
    badgeTexture:SetSize(136, 136)
    badgeTexture:SetTexture(resolveHomeTexturePath("HomeMythicBadge"))
    badgeTexture:SetVertexColor(1, 1, 1, 0.98)

    local summary = CreateFrame("Frame", nil, page.HighlightsPanel)
    summary:SetPoint("TOPLEFT", highlightsContent, "TOPLEFT", 154, 22)
    summary:SetPoint("TOPRIGHT", highlightsContent, "TOPRIGHT", 0, 0)
    summary:SetHeight(120)

    local bestLabel = summary:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    bestLabel:SetPoint("TOPLEFT", summary, "TOPLEFT", 0, -2)
    bestLabel:SetText("This Week's Best")
    bestLabel:SetTextColor(0.96, 0.93, 0.84, 1)

    local dungeonText = summary:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    dungeonText:SetPoint("TOPLEFT", bestLabel, "BOTTOMLEFT", 0, -6)
    dungeonText:SetWidth(260)
    dungeonText:SetJustifyH("LEFT")
    dungeonText:SetWordWrap(false)
    dungeonText:SetTextColor(0.36, 0.74, 1, 1)

    local keyValue = summary:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
    keyValue:SetPoint("TOPLEFT", dungeonText, "BOTTOMLEFT", 0, -16)
    keyValue:SetTextColor(0.84, 0.62, 1, 1)
    keyValue:SetScale(1.8)

    local scoreLabel = summary:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    scoreLabel:SetPoint("LEFT", keyValue, "RIGHT", 34, 8)
    scoreLabel:SetText("Score")

    local scoreValue = summary:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    scoreValue:SetPoint("TOPLEFT", scoreLabel, "BOTTOMLEFT", 0, -4)

    local timedLabel = summary:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    timedLabel:SetPoint("LEFT", scoreLabel, "RIGHT", 74, 0)
    timedLabel:SetText("Timed")

    local timedValue = summary:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    timedValue:SetPoint("TOPLEFT", timedLabel, "BOTTOMLEFT", 0, -4)

    local raidDivider = summary:CreateTexture(nil, "ARTWORK")
    raidDivider:SetPoint("TOPLEFT", summary, "TOPLEFT", 286, -8)
    raidDivider:SetPoint("BOTTOMLEFT", summary, "BOTTOMLEFT", 286, 8)
    raidDivider:SetWidth(1)
    raidDivider:SetColorTexture(0.44, 0.34, 0.16, 0.45)

    local raidLabel = summary:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    raidLabel:SetPoint("TOPLEFT", raidDivider, "TOPRIGHT", 12, 0)
    raidLabel:SetText("Raid")

    local raidNameValue = summary:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    raidNameValue:SetPoint("TOPLEFT", raidLabel, "BOTTOMLEFT", 0, -4)
    raidNameValue:SetWidth(166)
    raidNameValue:SetJustifyH("LEFT")
    raidNameValue:SetWordWrap(true)

    local bossesLabel = summary:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
    bossesLabel:SetPoint("TOPLEFT", raidNameValue, "BOTTOMLEFT", 0, -10)
    bossesLabel:SetText(trimString(mythicRaidSnapshot.raidHighlight and mythicRaidSnapshot.raidHighlight.detailLabel) ~= "" and mythicRaidSnapshot.raidHighlight.detailLabel or "Status")

    local bossesValue = summary:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    bossesValue:SetPoint("TOPLEFT", bossesLabel, "BOTTOMLEFT", 0, -4)

    local footerLine = page.HighlightsPanel:CreateTexture(nil, "ARTWORK")
    footerLine:SetPoint("BOTTOMLEFT", highlightsContent, "BOTTOMLEFT", 0, 18)
    footerLine:SetPoint("BOTTOMRIGHT", highlightsContent, "BOTTOMRIGHT", 0, 18)
    footerLine:SetHeight(1)
    footerLine:SetColorTexture(0.44, 0.34, 0.16, 0.45)

    if not bestMythic then
        dungeonText:SetText("No Mythic+ data available")
        keyValue:SetText("-")
        scoreValue:SetText("-")
        timedValue:SetText("-")
        raidNameValue:SetText(trimString(mythicRaidSnapshot.raidHighlight and mythicRaidSnapshot.raidHighlight.raidName) ~= "" and mythicRaidSnapshot.raidHighlight.raidName or "No live raid data")
        bossesValue:SetText(trimString(mythicRaidSnapshot.raidHighlight and mythicRaidSnapshot.raidHighlight.bossesKilled) ~= "" and mythicRaidSnapshot.raidHighlight.bossesKilled or "Waiting for signups")
    else
        local dungeon, keyLevel = splitBestKeyText(bestMythic.key)
        dungeonText:SetText(dungeon ~= "" and dungeon or "No Mythic+ data available")
        keyValue:SetText(keyLevel ~= "" and ("+" .. keyLevel) or "-")
        scoreValue:SetText(bestMythic.score or "-")
        timedValue:SetText(bestMythic.timed or "-")
        raidNameValue:SetText(trimString(mythicRaidSnapshot.raidHighlight and mythicRaidSnapshot.raidHighlight.raidName) ~= "" and mythicRaidSnapshot.raidHighlight.raidName or "No live raid data")
        bossesValue:SetText(trimString(mythicRaidSnapshot.raidHighlight and mythicRaidSnapshot.raidHighlight.bossesKilled) ~= "" and mythicRaidSnapshot.raidHighlight.bossesKilled or "Waiting for signups")
    end

    page.LootPanel = CreatePanel(page, addon.NewUILayout.Home.Lower.Loot, "Recent Loot / Upgrades", { backgroundTextureKey = "HomePanelBackground", borderTextureKey = "HomePanelBorder", footerLabel = "BIS Tracking", linkText = "View Loot History", onLinkClick = function() OpenLegacyMainFrameView("bis") end })
    local recentLootSnapshot = BuildRecentLootSnapshot()

    if recentLootSnapshot.rows and #recentLootSnapshot.rows > 0 then
        for index, row in ipairs(recentLootSnapshot.rows) do
            local lineOffset = -((index - 1) * 24)
            CreateLootLine(page.LootPanel.Content, lineOffset, row)
        end
    else
        page.LootLine1 = CreateLootLine(page.LootPanel.Content, 0, "No recent loot or upgrades tracked yet.")
        page.LootLine2 = CreateLootLine(page.LootPanel.Content, -24, "BIS loot history will populate this panel automatically.")
    end

    page.FooterPanel = CreateFooter(page, addon.NewUILayout.Home.Footer.Progress)

    self.lastSnapshot = homeSnapshot

    return page
end

function home:ShowPreview(snapshot)
    if not addon.NewUIShell then
        return nil
    end

    if not self.previewShell then
        self.previewShell = addon.NewUIShell:Create(UIParent)
    end

    if not self.previewShell or not self.previewShell.contentHost then
        return nil
    end

    self.previewShell:Show()
    return addon.NewUIRoot and addon.NewUIRoot:RenderHome(self.previewShell.contentHost, snapshot)
end

return home