local _, addonTable = ...
---@type MKA
local MKA = addonTable.MKA
local core = addonTable
local TRANSLATE = core.TRANSLATE or function(msg) return msg end

core.CharacterTab = core.CharacterTab or {}
local CharacterTab = core.CharacterTab
local SetSecureButtonAttributes = core.SetSecureButtonAttributes or function(button, ...)
    if not (button and button.SetAttribute) then
        return false
    end

    local count = select("#", ...)
    for index = 1, count, 2 do
        local key = select(index, ...)
        if key ~= nil then
            local value = select(index + 1, ...)
            button:SetAttribute(key, value)
        end
    end
    return true
end

local CHARACTER_CARD_MIN_WIDTH = 372
local CHARACTER_CARD_GAP_Y = 14
local PLAYER_INFO_TITLE_OFFSET_Y = -20
local PLAYER_INFO_TITLE_INSET_RIGHT = 8
local PLAYER_INFO_TITLE_TO_SECTION_GAP = 16
local CURRENT_CHARACTER_CARD_HEIGHT = 100
local CURRENT_CHARACTER_CARD_GAP = 12
local CURRENT_CHARACTER_CARD_BG_ALPHA = 0.20
local CURRENT_CHARACTER_CARD_TOP_HEIGHT = 65
local CURRENT_CHARACTER_CARD_BOTTOM_HEIGHT = 35
local CURRENT_CHARACTER_CARD_INNER_INSET = 5
local CURRENT_CHARACTER_CARD_DIVIDER_HEIGHT = 1
local CURRENT_CHARACTER_CARD_DIVIDER_INSET = 5
local CURRENT_CHARACTER_CARD_DIVIDER_ALPHA = 0.38
local CURRENT_CHARACTER_CARD_CONTROL_INSET_X = 14
local CURRENT_CHARACTER_CARD_TOP_LABEL_Y = -9
local CURRENT_CHARACTER_CARD_INFO_ROW_Y = 10
local CURRENT_CHARACTER_CARD_BOTTOM_ROW_Y = 0
local CURRENT_CHARACTER_CARD_TELEPORT_WIDTH = 24
local CURRENT_CHARACTER_CARD_TELEPORT_RIGHT_NUDGE = 4
local CURRENT_CHARACTER_CARD_ROLE_TOGGLE_WIDTH = 40
local CURRENT_CHARACTER_CARD_ROLE_TOGGLE_GAP = 8
local CHARACTER_SECTION_TITLE_CARD_GAP = 14
local WARBAND_CHARACTER_CARDS_BOTTOM_GAP = 20
local MYTHIC_STATS_CARD_BORDER_COLOR = { r = 1.00, g = 0.82, b = 0.00, a = 1.00 }
local MYTHIC_STATS_CARD_BG_COLOR = { r = 1.00, g = 0.82, b = 0.00, a = 0.20 }
local WEEKLY_REPORT_RUN_LEVEL_COLORS = {
    timed = "ff00ff00",
    overtime = "ffff4040",
    muted = "ff8a8a8a",
    accent = "ffffb80d",
    champion = "ff1eff00",
    hero = "ffa335ee",
    myth = "ffffd100",
}
local WEEKLY_VAULT_REWARD_ICON_SIZE = 14
local WEEKLY_VAULT_REWARD_ICON_READY = string.format("|A:UI-LFG-ReadyMark:%d:%d|a", WEEKLY_VAULT_REWARD_ICON_SIZE, WEEKLY_VAULT_REWARD_ICON_SIZE)
local WEEKLY_VAULT_REWARD_ICON_INCOMPLETE = string.format("|A:UI-LFG-DeclineMark:%d:%d|a", WEEKLY_VAULT_REWARD_ICON_SIZE, WEEKLY_VAULT_REWARD_ICON_SIZE)
local NATIVE_ROLE_ATLAS_BY_KEY = {
    TANK = "UI-LFG-RoleIcon-Tank",
    HEAL = "UI-LFG-RoleIcon-Healer",
    DPS = "UI-LFG-RoleIcon-DPS",
}

local DungeonTeleport = core.DungeonTeleport or {}
local AddDungeonTeleportTooltipLine = DungeonTeleport.AddTeleportTooltipLine or function() end
local AttachDungeonTeleportDialogTrigger = DungeonTeleport.AttachDialogTrigger or function() end
local GetDungeonTeleportMacroText = DungeonTeleport.GetMacroText or function() return nil end
local GetDungeonTeleportStatus = DungeonTeleport.GetStatus or function() return nil end

local GetClassColorByToken = core.GetClassColorByToken or function() return 1, 0.82, 0 end
local HideKeystoneTooltip = core.HideKeystoneTooltip or function()
    if GameTooltip then
        GameTooltip:Hide()
    end
end
local ResetCardSubFrameSkin = core.ResetCardSubFrameSkin or function() end
local UpdateCharacterCardsScrollState = core.UpdateCharacterCardsScrollState or function() end

local function TrySetAtlas(texture, atlas, useAtlasSize)
    if not texture or not atlas or not texture.SetAtlas then
        return false
    end

    local ok = pcall(texture.SetAtlas, texture, atlas, useAtlasSize)
    return ok == true
end

local function CreateSolidTexture(parent, layer, subLevel)
    local texture = parent:CreateTexture(nil, layer or "ARTWORK", nil, subLevel or 0)
    texture:SetTexture("Interface\\Buttons\\WHITE8X8")
    return texture
end

local function WrapColor(text, color)
    return string.format("|c%s%s|r", color or "ffffffff", tostring(text or ""))
end

local function GetWeeklyVaultRewardLabel(level)
    local rewardLevel = math.min(math.max(tonumber(level) or 0, 0), 10)
    if rewardLevel >= 10 then
        return TRANSLATE("UPGRADE_TRACK.MYTH")
    end
    if rewardLevel >= 3 then
        return TRANSLATE("UPGRADE_TRACK.HERO")
    end
    if rewardLevel >= 2 then
        return TRANSLATE("UPGRADE_TRACK.CHAMPION")
    end
    return ""
end

local function GetWeeklyVaultRewardColor(level)
    local rewardLevel = math.min(math.max(tonumber(level) or 0, 0), 10)
    if rewardLevel >= 10 then
        return WEEKLY_REPORT_RUN_LEVEL_COLORS.myth
    end
    if rewardLevel >= 3 then
        return WEEKLY_REPORT_RUN_LEVEL_COLORS.hero
    end
    if rewardLevel >= 2 then
        return WEEKLY_REPORT_RUN_LEVEL_COLORS.champion
    end
    return WEEKLY_REPORT_RUN_LEVEL_COLORS.accent
end

local function FormatWeeklyRunLevel(run)
    if type(run) ~= "table" then
        return WrapColor("-", WEEKLY_REPORT_RUN_LEVEL_COLORS.muted)
    end

    local color = run.overtime and WEEKLY_REPORT_RUN_LEVEL_COLORS.overtime or WEEKLY_REPORT_RUN_LEVEL_COLORS.timed
    return WrapColor("+" .. tostring(tonumber(run.level) or 0), color)
end

local function FormatWeeklyVaultRewardPart(label, run)
    local labelText = WrapColor(label .. "：", WEEKLY_REPORT_RUN_LEVEL_COLORS.accent)
    if type(run) ~= "table" then
        return string.format("%s %s%s", WEEKLY_VAULT_REWARD_ICON_INCOMPLETE, labelText, WrapColor(TRANSLATE("GREAT_VAULT.REWARD.INCOMPLETE"), WEEKLY_REPORT_RUN_LEVEL_COLORS.muted))
    end

    local rewardLabel = GetWeeklyVaultRewardLabel(run.level)
    if rewardLabel == "" then
        return string.format("%s %s%s", WEEKLY_VAULT_REWARD_ICON_INCOMPLETE, labelText, WrapColor(TRANSLATE("GREAT_VAULT.REWARD.INCOMPLETE"), WEEKLY_REPORT_RUN_LEVEL_COLORS.muted))
    end

    return string.format("%s %s%s", WEEKLY_VAULT_REWARD_ICON_READY, labelText, WrapColor(rewardLabel, GetWeeklyVaultRewardColor(run.level)))
end

local function FormatWeeklyVaultRewardSummary(weeklyReport)
    local thresholds = {
        { index = 1, label = TRANSLATE("GREAT_VAULT.REWARD.INITIAL") },
        { index = 4, label = TRANSLATE("GREAT_VAULT.REWARD.FOUR_RUN") },
        { index = 8, label = TRANSLATE("GREAT_VAULT.REWARD.EIGHT_RUN") },
    }
    local vaultParts = {}
    for _, threshold in ipairs(thresholds) do
        vaultParts[#vaultParts + 1] = FormatWeeklyVaultRewardPart(threshold.label, weeklyReport.topRuns[threshold.index])
    end
    return string.format("%s  %s", TRANSLATE("GREAT_VAULT.TITLE"), table.concat(vaultParts, "  "))
end

local function BuildCurrentCharacterWeeklyReport(owner)
    local report = {
        topRuns = {},
        dungeonGroups = {},
        totalCount = 0,
        unavailable = false,
    }

    if not C_MythicPlus or not C_MythicPlus.GetRunHistory then
        report.unavailable = true
        return report
    end

    local ok, runHistory = pcall(C_MythicPlus.GetRunHistory, false, true)
    if not ok or type(runHistory) ~= "table" then
        report.unavailable = true
        return report
    end

    local sortedRuns = {}
    local groupsByMapID = {}
    for _, run in ipairs(runHistory) do
        local mapID = tonumber(run.mapChallengeModeID)
        local level = tonumber(run.level) or 0
        if mapID and level > 0 then
            local runData = {
                mapID = mapID,
                level = level,
                overtime = run.completed ~= true,
            }
            sortedRuns[#sortedRuns + 1] = runData
            report.totalCount = report.totalCount + 1

            local dungeonGroup = groupsByMapID[mapID]
            if not dungeonGroup then
                dungeonGroup = {
                    mapID = mapID,
                    dungeonName = owner and owner.GetAbbreviatedDungeonName and owner:GetAbbreviatedDungeonName(mapID) or tostring(mapID),
                    runs = {},
                }
                groupsByMapID[mapID] = dungeonGroup
                report.dungeonGroups[#report.dungeonGroups + 1] = dungeonGroup
            end
            dungeonGroup.runs[#dungeonGroup.runs + 1] = runData
        end
    end

    table.sort(sortedRuns, function(left, right)
        if left.level ~= right.level then
            return left.level > right.level
        end
        if left.overtime ~= right.overtime then
            return right.overtime
        end
        return left.mapID < right.mapID
    end)

    for index = 1, math.min(8, #sortedRuns) do
        report.topRuns[index] = sortedRuns[index]
    end

    for _, dungeonGroup in ipairs(report.dungeonGroups) do
        table.sort(dungeonGroup.runs, function(left, right)
            if left.level ~= right.level then
                return left.level > right.level
            end
            return not left.overtime and right.overtime
        end)
    end
    table.sort(report.dungeonGroups, function(left, right)
        return (left.dungeonName or "") < (right.dungeonName or "")
    end)

    return report
end

local function CreateRoundedInsetBackdrop(parent)
    local frameParent = parent and parent:GetParent() or parent
    local frame = CreateFrame("Frame", nil, frameParent, "BackdropTemplate")
    frame:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, 0)
    frame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", 0, 0)
    frame:EnableMouse(false)
    if frame.SetFrameLevel and parent.GetFrameLevel then
        frame:SetFrameLevel(math.max((parent:GetFrameLevel() or 1) - 1, 0))
    end
    if frame.SetBackdrop then
        frame:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            tile = false,
            edgeSize = 10,
            insets = { left = 3, right = 3, top = 3, bottom = 3 },
        })
        frame:SetBackdropColor(0, 0, 0, 0.78)
        frame:SetBackdropBorderColor(1, 1, 1, 0.16)
    end
    parent:HookScript("OnShow", function()
        frame:Show()
    end)
    parent:HookScript("OnHide", function()
        frame:Hide()
    end)
    return frame
end

local function ApplyHorizontalGradient(texture, startR, startG, startB, startA, endR, endG, endB, endA)
    if not texture then
        return
    end

    if texture.SetGradient and CreateColor then
        texture:SetGradient("HORIZONTAL", CreateColor(startR, startG, startB, startA), CreateColor(endR, endG, endB, endA))
    elseif texture.SetGradientAlpha then
        texture:SetGradientAlpha("HORIZONTAL", startR, startG, startB, startA, endR, endG, endB, endA)
    else
        texture:SetVertexColor(endR, endG, endB, math.max(startA or 0, endA or 0) * 0.55)
    end
end

local function GetCurrentCharacterKey()
    local name = UnitName("player") or "Unknown"
    local realm = GetRealmName() or "Unknown"
    return string.format("%s-%s", name, realm)
end

local function GetMaxCharacterLevel()
    if GetMaxPlayerLevel then
        local ok, maxLevel = pcall(GetMaxPlayerLevel)
        maxLevel = tonumber(maxLevel)
        if ok and maxLevel and maxLevel > 0 then
            return maxLevel
        end
    end

    local globalMaxLevel = tonumber(_G.MAX_PLAYER_LEVEL)
    if globalMaxLevel and globalMaxLevel > 0 then
        return globalMaxLevel
    end

    if type(_G.MAX_PLAYER_LEVEL_TABLE) == "table" then
        local maxLevel = 0
        for _, level in pairs(_G.MAX_PLAYER_LEVEL_TABLE) do
            level = tonumber(level)
            if level and level > maxLevel then
                maxLevel = level
            end
        end
        if maxLevel > 0 then
            return maxLevel
        end
    end

    return nil
end

local function CharacterHasAnyKeystoneData(character)
    return character and (
        (tonumber(character.keyLevel) and tonumber(character.keyLevel) > 0)
        or character.keystoneLink
        or character.hasKeystone
    )
end

local function IsWarbandMaxLevelCharacter(character)
    if not character then
        return false
    end

    local level = tonumber(character.level)
    local maxLevel = GetMaxCharacterLevel()
    if level and maxLevel then
        return level >= maxLevel
    end

    -- Older records did not store level; keep known keystone/rating records visible until they refresh with level.
    return level == nil and (CharacterHasAnyKeystoneData(character) or (tonumber(character.rating) or 0) > 0)
end

local function GetCharacterCardKeystoneText(owner, character)
    if character and character.keystoneLink then
        return character.keystoneLink
    end
    if CharacterHasAnyKeystoneData(character) then
        return owner:GetCharacterKeystoneText(character)
    end
    return TRANSLATE("COMMON.NO_INFORMATION")
end

local function GetCurrentCharacterEntry(owner)
    local key = GetCurrentCharacterKey()
    local characters = owner and owner.db and owner.db.global and owner.db.global.characters
    local character = characters and characters[key]
    if character then
        return {
            key = key,
            data = character,
        }
    end

    return {
        key = key,
        data = {
            name = key,
            class = select(2, UnitClass("player")),
            realm = GetRealmName(),
            level = UnitLevel and UnitLevel("player") or nil,
        },
    }
end

local function GetPlainCurrentCharacterName(character, characterKey)
    local name = character and character.name or characterKey or (UnitName("player") or TRANSLATE("COMMON.UNKNOWN"))
    if type(name) ~= "string" or name == "" then
        name = UnitName("player") or TRANSLATE("COMMON.UNKNOWN")
    end

    if name:find("-", 1, true) then
        return name
    end

    local realm = character and character.realm or GetRealmName()
    if type(realm) == "string" and realm ~= "" then
        return string.format("%s-%s", name, realm)
    end

    return name
end

local function GetCurrentSpecializationRoleKey()
    if not GetSpecialization or not GetSpecializationRole then
        return "DPS"
    end

    local specializationIndex = GetSpecialization()
    local specializationRole = specializationIndex and GetSpecializationRole(specializationIndex)
    if specializationRole == "TANK" then
        return "TANK"
    end
    if specializationRole == "HEALER" then
        return "HEAL"
    end

    return "DPS"
end

local function NormalizeCurrentCharacterRoles(owner, character)
    local roles = character and character.roles
    if character and character.rolesManuallyConfigured and type(roles) == "table" then
        for _, roleKey in ipairs(owner:GetRoleOrder()) do
            if roles[roleKey] then
                return roles
            end
        end
    end

    roles = {}
    if character and type(character.role) == "string" then
        for _, roleKey in ipairs(owner:GetRoleOrder()) do
            if character.role == roleKey then
                roles[roleKey] = true
                character.roles = roles
                character.role = nil
                character.rolesManuallyConfigured = true
                return roles
            end
        end
    end

    roles[GetCurrentSpecializationRoleKey()] = true
    if character then
        character.roles = roles
        character.role = nil
    end
    return roles
end

local function CreateCurrentCharacterKeyButton(parent)
    local keyButton = CreateFrame("Button", nil, parent)
    keyButton:SetHeight(24)
    keyButton:EnableMouse(true)
    ResetCardSubFrameSkin(keyButton)

    local keyBackdrop = CreateRoundedInsetBackdrop(keyButton)

    local keyText = keyButton:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    keyText:SetPoint("LEFT", keyButton, "LEFT", 8, 0)
    keyText:SetPoint("RIGHT", keyButton, "RIGHT", -8, 0)
    keyText:SetJustifyH("CENTER")
    if core.ApplyFont then
        core.ApplyFont(keyText, 12, "")
    end

    local keyHover = keyButton:CreateTexture(nil, "HIGHLIGHT")
    keyHover:SetPoint("TOPLEFT", keyButton, "TOPLEFT", 3, -3)
    keyHover:SetPoint("BOTTOMRIGHT", keyButton, "BOTTOMRIGHT", -3, 3)
    keyHover:SetTexture("Interface\\Buttons\\WHITE8X8")
    keyHover:SetVertexColor(1, 1, 1, 0.06)

    keyButton:SetScript("OnEnter", function(button)
        local link = button.keystoneLink
        if not link then
            return
        end

        GameTooltip:SetOwner(button, "ANCHOR_CURSOR")
        GameTooltip:SetHyperlink(link)
        GameTooltip:Show()
    end)
    keyButton:SetScript("OnLeave", function()
        HideKeystoneTooltip()
    end)
    keyButton:SetScript("OnClick", function(button, mouseButton)
        local link = button.keystoneLink
        if not link then
            return
        end

        if HandleModifiedItemClick and HandleModifiedItemClick(link) then
            return
        end

        if mouseButton == "LeftButton" and SetItemRef then
            SetItemRef(link, link, mouseButton, button)
        end
    end)

    keyButton.text = keyText
    keyButton.roundedBackdrop = keyBackdrop
    return keyButton
end

local function CreateCurrentCharacterTeleportButton(parent)
    local button = CreateFrame("Button", nil, parent, "SecureActionButtonTemplate")
    button:RegisterForClicks("AnyUp", "AnyDown")
    SetSecureButtonAttributes(button, "type", "macro", "type1", "macro")
    ResetCardSubFrameSkin(button)

    local roundedBackdrop = CreateRoundedInsetBackdrop(button)

    local pushedTexture = button:CreateTexture(nil, "HIGHLIGHT")
    pushedTexture:SetPoint("TOPLEFT", button, "TOPLEFT", 3, -3)
    pushedTexture:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -3, 3)
    pushedTexture:SetTexture("Interface\\Buttons\\WHITE8X8")
    pushedTexture:SetVertexColor(1, 1, 1, 0.06)
    if button.SetPushedTexture then
        button:SetPushedTexture(pushedTexture)
    end

    local icon = button:CreateTexture(nil, "OVERLAY")
    icon:ClearAllPoints()
    icon:SetPoint("CENTER", button, "CENTER", 0, 0)
    icon:SetSize(16, 16)
    if icon.SetAtlas then
        icon:SetAtlas("MagePortalAlliance", false)
    end
    icon:SetVertexColor(1, 1, 1, 0.92)

    button:SetScript("OnEnter", function(teleportButton)
        local mapID = teleportButton.mapID
        if not mapID then
            return
        end

        GameTooltip:SetOwner(teleportButton, "ANCHOR_CURSOR")
        AddDungeonTeleportTooltipLine(GameTooltip, mapID)
        GameTooltip:Show()
    end)
    button:SetScript("OnLeave", function()
        HideKeystoneTooltip()
        button.isPressed = nil
        icon:ClearAllPoints()
        icon:SetPoint("CENTER", button, "CENTER", 0, 0)
    end)
    button:SetScript("OnMouseDown", function()
        button.isPressed = true
        icon:ClearAllPoints()
        icon:SetPoint("CENTER", button, "CENTER", 1, -1)
    end)
    button:SetScript("OnMouseUp", function()
        button.isPressed = nil
        icon:ClearAllPoints()
        icon:SetPoint("CENTER", button, "CENTER", 0, 0)
    end)
    AttachDungeonTeleportDialogTrigger(MKA, button)

    button.icon = icon
    button.roundedBackdrop = roundedBackdrop
    button.pushedTexture = pushedTexture
    return button
end

local function SkinNativeCheckButton(button, checkSize)
    if not button then
        return
    end

    checkSize = checkSize or 22
    local textures = {
        button:GetNormalTexture(),
        button:GetPushedTexture(),
        button:GetHighlightTexture(),
        button:GetCheckedTexture(),
        button:GetDisabledCheckedTexture(),
    }

    for _, texture in ipairs(textures) do
        if texture then
            texture:ClearAllPoints()
            texture:SetPoint("LEFT", button, "LEFT", 0, 0)
            texture:SetSize(checkSize, checkSize)
        end
    end
end

local function CreateNativeCurrentCharacterCheckButton(parent, labelText, width, onClick)
    local button = CreateFrame("CheckButton", nil, parent, "UICheckButtonTemplate")
    button:SetSize(width, 22)
    button:EnableMouse(true)
    SkinNativeCheckButton(button, 22)

    local label = button:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    label:SetPoint("LEFT", button, "LEFT", 25, 0)
    label:SetPoint("RIGHT", button, "RIGHT", 0, 0)
    label:SetJustifyH("LEFT")
    label:SetText(labelText or "")
    label:SetTextColor(1, 1, 1, 0.92)
    if core.ApplyFont then
        core.ApplyFont(label, 12, "")
    end

    button:SetScript("OnClick", function(self)
        if self.mkaLocked then
            self:SetChecked(true)
            return
        end

        if onClick then
            onClick(self:GetChecked() == true)
        end
    end)

    button.SetLocked = function(self, locked)
        self.mkaLocked = locked and true or false
        self:SetChecked(self.mkaLocked or self:GetChecked())
        self:EnableMouse(not self.mkaLocked)
        if self.label then
            self.label:SetTextColor(1, 1, 1, self.mkaLocked and 0.70 or 0.92)
        end
    end

    button.SetLabelText = function(self, text)
        if self.label then
            self.label:SetText(text or "")
        end
    end

    button.label = label
    return button
end

local function CreateNativeRoleCheckButton(parent, roleKey, onClick)
    local button = CreateNativeCurrentCharacterCheckButton(parent, "", CURRENT_CHARACTER_CARD_ROLE_TOGGLE_WIDTH, onClick)
    local icon = button:CreateTexture(nil, "OVERLAY")
    icon:SetPoint("LEFT", button, "LEFT", 23, 0)
    icon:SetSize(20, 20)
    if not TrySetAtlas(icon, NATIVE_ROLE_ATLAS_BY_KEY[roleKey], false) then
        icon:SetTexture("Interface\\LFGFrame\\UI-LFG-ICON-ROLES")
    end
    icon:Show()

    button.roleIcon = icon
    return button
end

local function CreateCurrentCharacterTopControls(card, topSection)
    local insetX = CURRENT_CHARACTER_CARD_CONTROL_INSET_X

    local teleportButton = CreateCurrentCharacterTeleportButton(topSection)
    teleportButton:SetPoint("BOTTOMRIGHT", topSection, "BOTTOMRIGHT", -insetX + CURRENT_CHARACTER_CARD_TELEPORT_RIGHT_NUDGE, CURRENT_CHARACTER_CARD_INFO_ROW_Y)
    teleportButton:SetSize(CURRENT_CHARACTER_CARD_TELEPORT_WIDTH, 24)

    local nameLabel = topSection:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    nameLabel:SetPoint("TOPLEFT", topSection, "TOPLEFT", insetX, CURRENT_CHARACTER_CARD_TOP_LABEL_Y + 1)
    nameLabel:SetJustifyH("LEFT")
    if core.ApplyFont then
        core.ApplyFont(nameLabel, 14, "OUTLINE")
    end

    local armorLabel = topSection:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    armorLabel:SetPoint("TOPRIGHT", topSection, "TOPRIGHT", -insetX, CURRENT_CHARACTER_CARD_TOP_LABEL_Y + 1)
    armorLabel:SetWidth(120)
    armorLabel:SetJustifyH("RIGHT")
    armorLabel:SetTextColor(0.86, 0.86, 0.86, 0.92)
    if core.ApplyFont then
        core.ApplyFont(armorLabel, 11, "")
    end

    local ratingLabel = topSection:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    ratingLabel:SetPoint("LEFT", nameLabel, "RIGHT", 6, 0)
    ratingLabel:SetPoint("RIGHT", armorLabel, "LEFT", -8, 0)
    ratingLabel:SetJustifyH("LEFT")
    if core.ApplyFont then
        core.ApplyFont(ratingLabel, 14, "OUTLINE")
    end

    local keyButton = CreateCurrentCharacterKeyButton(topSection)
    keyButton:SetPoint("BOTTOMLEFT", topSection, "BOTTOMLEFT", insetX, CURRENT_CHARACTER_CARD_INFO_ROW_Y)
    keyButton:SetPoint("RIGHT", teleportButton, "LEFT", -10, 0)

    card.nameLabel = nameLabel
    card.ratingLabel = ratingLabel
    card.armorLabel = armorLabel
    card.keyButton = keyButton
    card.keyText = keyButton.text
    card.teleportButton = teleportButton
    card.teleportIcon = teleportButton.icon
end

local function CreateCurrentCharacterBottomControls(card, bottomSection)
    local showInGroup = CreateNativeCurrentCharacterCheckButton(bottomSection, TRANSLATE("CHARACTER.JOIN_CARPOOL_LIST"), 142, function(value)
        if card.characterKey then
            MKA:UpdateCharacterSetting(card.characterKey, "showInGroup", value)
        end
    end)
    showInGroup:SetPoint("LEFT", bottomSection, "LEFT", CURRENT_CHARACTER_CARD_CONTROL_INSET_X, CURRENT_CHARACTER_CARD_BOTTOM_ROW_Y)

    local rolePrompt = bottomSection:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    rolePrompt:SetPoint("LEFT", bottomSection, "LEFT", CURRENT_CHARACTER_CARD_CONTROL_INSET_X + 2, CURRENT_CHARACTER_CARD_BOTTOM_ROW_Y)
    rolePrompt:SetWidth(142)
    rolePrompt:SetHeight(22)
    rolePrompt:SetJustifyH("LEFT")
    rolePrompt:SetText(TRANSLATE("ROLE.SELECTION"))
    rolePrompt:SetTextColor(1, 1, 1, 0.96)
    if core.ApplyFont then
        core.ApplyFont(rolePrompt, 12, "")
    end
    rolePrompt:Hide()

    local rolesAnchor = CreateFrame("Frame", nil, bottomSection)
    rolesAnchor:SetPoint("RIGHT", bottomSection, "RIGHT", -CURRENT_CHARACTER_CARD_CONTROL_INSET_X, CURRENT_CHARACTER_CARD_BOTTOM_ROW_Y)
    rolesAnchor:SetSize((CURRENT_CHARACTER_CARD_ROLE_TOGGLE_WIDTH * 3) + (CURRENT_CHARACTER_CARD_ROLE_TOGGLE_GAP * 2), 22)

    local roleToggles = {}
    local previousToggle = nil
    for _, roleKey in ipairs(MKA:GetRoleOrder()) do
        local toggle = CreateNativeRoleCheckButton(bottomSection, roleKey, function(value)
            if card.characterKey then
                MKA:UpdateCharacterRole(card.characterKey, roleKey, value)
                local updatedCharacter = MKA.db.global.characters[card.characterKey]
                local updatedRoles = MKA:NormalizeRoles(updatedCharacter)
                for _, syncRoleKey in ipairs(MKA:GetRoleOrder()) do
                    card.roles[syncRoleKey]:SetChecked(updatedRoles[syncRoleKey] or false)
                end
            end
        end)
        if previousToggle then
            toggle:SetPoint("LEFT", previousToggle, "RIGHT", CURRENT_CHARACTER_CARD_ROLE_TOGGLE_GAP, 0)
        else
            toggle:SetPoint("LEFT", rolesAnchor, "LEFT", 0, 0)
        end
        previousToggle = toggle
        roleToggles[roleKey] = toggle
    end

    card.showInGroupToggle = showInGroup
    card.rolePromptLabel = rolePrompt
    card.roles = roleToggles
end

local function CreateCurrentCharacterCardSections(card)
    local inset = CURRENT_CHARACTER_CARD_INNER_INSET

    local topSection = CreateFrame("Frame", nil, card)
    topSection:SetPoint("TOPLEFT", card, "TOPLEFT", inset, -inset)
    topSection:SetPoint("TOPRIGHT", card, "TOPRIGHT", -inset, -inset)
    topSection:SetHeight(CURRENT_CHARACTER_CARD_TOP_HEIGHT)

    local topBg = CreateSolidTexture(topSection, "BACKGROUND", -1)
    topBg:SetAllPoints(topSection)
    topBg:SetVertexColor(0, 0, 0, 0)

    local bottomSection = CreateFrame("Frame", nil, card)
    bottomSection:SetPoint("TOPLEFT", topSection, "BOTTOMLEFT", 0, 0)
    bottomSection:SetPoint("TOPRIGHT", topSection, "BOTTOMRIGHT", 0, 0)
    bottomSection:SetPoint("BOTTOMLEFT", card, "BOTTOMLEFT", inset, inset)
    bottomSection:SetPoint("BOTTOMRIGHT", card, "BOTTOMRIGHT", -inset, inset)

    local bottomBg = CreateSolidTexture(bottomSection, "BACKGROUND", -1)
    bottomBg:SetAllPoints(bottomSection)
    bottomBg:SetVertexColor(0, 0, 0, 0)

    local divider = CreateFrame("Frame", nil, card)
    divider:SetPoint("LEFT", topSection, "BOTTOMLEFT", CURRENT_CHARACTER_CARD_DIVIDER_INSET, 0)
    divider:SetPoint("RIGHT", topSection, "BOTTOMRIGHT", -CURRENT_CHARACTER_CARD_DIVIDER_INSET, 0)
    divider:SetHeight(6)

    local dividerShadow = CreateSolidTexture(divider, "ARTWORK", 0)
    dividerShadow:SetPoint("LEFT", divider, "LEFT", 0, -1)
    dividerShadow:SetPoint("RIGHT", divider, "RIGHT", 0, -1)
    dividerShadow:SetHeight(1)
    dividerShadow:SetVertexColor(0, 0, 0, 0)

    local dividerLeft = CreateSolidTexture(divider, "ARTWORK", 1)
    dividerLeft:SetPoint("LEFT", divider, "LEFT", 0, 0)
    dividerLeft:SetPoint("RIGHT", divider, "CENTER", 0, 0)
    dividerLeft:SetHeight(CURRENT_CHARACTER_CARD_DIVIDER_HEIGHT)

    local dividerRight = CreateSolidTexture(divider, "ARTWORK", 1)
    dividerRight:SetPoint("LEFT", divider, "CENTER", 0, 0)
    dividerRight:SetPoint("RIGHT", divider, "RIGHT", 0, 0)
    dividerRight:SetHeight(CURRENT_CHARACTER_CARD_DIVIDER_HEIGHT)

    local dividerSoftLeft = CreateSolidTexture(divider, "ARTWORK", 0)
    dividerSoftLeft:SetPoint("LEFT", dividerLeft, "LEFT", 0, 1)
    dividerSoftLeft:SetPoint("RIGHT", dividerLeft, "RIGHT", 0, 1)
    dividerSoftLeft:SetHeight(1)

    local dividerSoftRight = CreateSolidTexture(divider, "ARTWORK", 0)
    dividerSoftRight:SetPoint("LEFT", dividerRight, "LEFT", 0, 1)
    dividerSoftRight:SetPoint("RIGHT", dividerRight, "RIGHT", 0, 1)
    dividerSoftRight:SetHeight(1)

    CreateCurrentCharacterTopControls(card, topSection)
    CreateCurrentCharacterBottomControls(card, bottomSection)

    card.topSection = topSection
    card.topBg = topBg
    card.bottomSection = bottomSection
    card.bottomBg = bottomBg
    card.dividerShadow = dividerShadow
    card.dividerLeft = dividerLeft
    card.dividerRight = dividerRight
    card.dividerSoftLeft = dividerSoftLeft
    card.dividerSoftRight = dividerSoftRight
end

local function ShowMythicStatsTooltip(card)
    if not card or type(card.weeklyReport) ~= "table" then
        return
    end

    local report = card.weeklyReport
    GameTooltip:SetOwner(card, "ANCHOR_CURSOR")
    GameTooltip:ClearLines()
    GameTooltip:AddLine(TRANSLATE("CHARACTER.WEEKLY_REPORT.TITLE"), 1, 0.82, 0)

    if report.unavailable then
        GameTooltip:AddLine(TRANSLATE("CHARACTER.WEEKLY_REPORT.UNAVAILABLE"), 0.70, 0.70, 0.70, true)
        GameTooltip:Show()
        return
    end

    if report.totalCount <= 0 then
        GameTooltip:AddLine(TRANSLATE("CHARACTER.WEEKLY_REPORT.NO_RUNS"), 0.70, 0.70, 0.70, true)
        GameTooltip:Show()
        return
    end

    GameTooltip:AddDoubleLine(TRANSLATE("CHARACTER.WEEKLY_REPORT.RUN_COUNT"), tostring(report.totalCount), 0.82, 0.82, 0.82, 1, 1, 1)
    GameTooltip:AddLine(" ")
    GameTooltip:AddLine(TRANSLATE("CHARACTER.WEEKLY_REPORT.BY_DUNGEON"), 1, 0.82, 0)
    for _, dungeonGroup in ipairs(report.dungeonGroups or {}) do
        local runParts = {}
        for _, run in ipairs(dungeonGroup.runs or {}) do
            runParts[#runParts + 1] = FormatWeeklyRunLevel(run)
        end
        GameTooltip:AddDoubleLine(
            string.format("%s (%d)", dungeonGroup.dungeonName or TRANSLATE("DUNGEON.UNKNOWN"), #(dungeonGroup.runs or {})),
            table.concat(runParts, " "),
            0.92, 0.92, 0.92,
            1, 1, 1
        )
    end
    GameTooltip:Show()
end

local function CreateCurrentCharacterCard(parentFrame, frameName, cardType)
    local frameType = cardType == "mythicStats" and "Button" or "Frame"
    local card = CreateFrame(frameType, frameName, parentFrame, "BackdropTemplate")
    card.cardType = cardType
    card:EnableMouse(cardType == "mythicStats")
    if card.SetBackdrop then
        card:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
            tile = false,
            edgeSize = 14,
            insets = { left = 4, right = 4, top = 4, bottom = 4 },
        })
    end

    if cardType == "currentCharacter" then
        CreateCurrentCharacterCardSections(card)
    end

    if cardType ~= "currentCharacter" then
        local nameLabel = card:CreateFontString(nil, "OVERLAY", "GameFontNormal")
        nameLabel:SetPoint("TOPLEFT", card, "TOPLEFT", 18, -12)
        nameLabel:SetPoint("TOPRIGHT", card, "TOPRIGHT", -18, -12)
        nameLabel:SetJustifyH("LEFT")
        nameLabel:SetText(TRANSLATE("CHARACTER.CURRENT"))
        if core.ApplyFont then
            core.ApplyFont(nameLabel, 14, "OUTLINE")
        end
        card.nameLabel = nameLabel
    end

    if cardType == "mythicStats" then
        local totalLabel = card:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
        totalLabel:SetPoint("TOPRIGHT", card, "TOPRIGHT", -18, -13)
        totalLabel:SetWidth(150)
        totalLabel:SetJustifyH("RIGHT")
        if core.ApplyFont then
            core.ApplyFont(totalLabel, 12, "")
        end
        totalLabel:SetTextColor(1, 1, 1, 0.92)

        local vaultLabel = card:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        vaultLabel:SetPoint("TOPLEFT", card, "TOPLEFT", 18, -39)
        vaultLabel:SetPoint("TOPRIGHT", card, "TOPRIGHT", -18, -39)
        vaultLabel:SetJustifyH("LEFT")
        if core.ApplyFont then
            core.ApplyFont(vaultLabel, 11, "")
        end
        vaultLabel:SetTextColor(1, 1, 1, 0.92)

        local topRunsLabel = card:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
        topRunsLabel:SetPoint("TOPLEFT", card, "TOPLEFT", 18, -63)
        topRunsLabel:SetPoint("TOPRIGHT", card, "TOPRIGHT", -18, -63)
        topRunsLabel:SetJustifyH("LEFT")
        if topRunsLabel.SetWordWrap then
            topRunsLabel:SetWordWrap(false)
        end
        if core.ApplyFont then
            core.ApplyFont(topRunsLabel, 11, "")
        end
        topRunsLabel:SetTextColor(1, 1, 1, 0.92)

        local highlight = card:CreateTexture(nil, "HIGHLIGHT")
        if not TrySetAtlas(highlight, "campcollection-frame-hover", false) then
            highlight:SetTexture(nil)
        end
        highlight:SetPoint("TOPLEFT", card, "TOPLEFT", -1, 1)
        highlight:SetPoint("BOTTOMRIGHT", card, "BOTTOMRIGHT", 1, -1)
        highlight:SetBlendMode("ADD")
        highlight:SetVertexColor(1, 0.82, 0, 0.95)
        if card.SetHighlightTexture then
            card:SetHighlightTexture(highlight)
        end
        card.highlightTexture = highlight
        card.weeklyTotalLabel = totalLabel
        card.weeklyVaultLabel = vaultLabel
        card.weeklyTopRunsLabel = topRunsLabel
        card:SetScript("OnEnter", ShowMythicStatsTooltip)
        card:SetScript("OnLeave", function()
            HideKeystoneTooltip()
        end)
    end
    return card
end

function MKA:CreateCurrentCharacterCardFrame(parentFrame, titleFrame)
    local container = CreateFrame("Frame", "PlayerCurrentCharacterCardArea", parentFrame)
    container:SetPoint("TOP", titleFrame, "BOTTOM", 0, -CHARACTER_SECTION_TITLE_CARD_GAP)
    container:SetPoint("LEFT", parentFrame, "LEFT", 0, 0)
    container:SetPoint("RIGHT", parentFrame, "RIGHT", 0, 0)
    container:SetHeight(CURRENT_CHARACTER_CARD_HEIGHT)
    container:SetFrameLevel((titleFrame:GetFrameLevel() or parentFrame:GetFrameLevel() or 0) + 2)

    local leftCard = CreateCurrentCharacterCard(container, "PlayerCurrentCharacterCard", "currentCharacter")
    local rightCard = CreateCurrentCharacterCard(container, "PlayerCurrentMythicStatsCard", "mythicStats")
    leftCard:SetPoint("TOPLEFT", container, "TOPLEFT", 0, 0)
    rightCard:SetPoint("TOPRIGHT", container, "TOPRIGHT", 0, 0)

    container.currentCharacterCard = leftCard
    container.mythicStatsCard = rightCard
    container.leftCard = leftCard
    container.rightCard = rightCard
    self:LayoutCurrentCharacterCardFrame(container)
    return container
end

function MKA:LayoutCurrentCharacterCardFrame(container)
    if not container or not container.leftCard or not container.rightCard then
        return
    end

    local availableWidth = container:GetWidth() or 0
    if availableWidth <= 0 and container:GetParent() then
        availableWidth = container:GetParent():GetWidth() or 0
    end
    if availableWidth <= 0 then
        availableWidth = 900
    end

    local cardWidth = math.max(1, math.floor((availableWidth - CURRENT_CHARACTER_CARD_GAP) / 2))
    container:SetHeight(CURRENT_CHARACTER_CARD_HEIGHT)
    container.leftCard:SetSize(cardWidth, CURRENT_CHARACTER_CARD_HEIGHT)
    container.rightCard:SetSize(cardWidth, CURRENT_CHARACTER_CARD_HEIGHT)
end

function MKA:RefreshCurrentCharacterCard(container)
    if not container then
        return
    end

    self:LayoutCurrentCharacterCardFrame(container)

    local currentEntry = GetCurrentCharacterEntry(self)
    local character = currentEntry.data or {}
    character.showInGroup = true
    character.hide = false
    local roles = NormalizeCurrentCharacterRoles(self, character)
    local classToken = character.class or select(2, UnitClass("player"))
    local classR, classG, classB = GetClassColorByToken(classToken)

    local currentCharacterCard = container.currentCharacterCard or container.leftCard
    if currentCharacterCard then
        currentCharacterCard.characterKey = currentEntry.key

        if currentCharacterCard.SetBackdropColor then
            currentCharacterCard:SetBackdropColor(0, 0, 0, 0)
        end
        if currentCharacterCard.SetBackdropBorderColor then
            currentCharacterCard:SetBackdropBorderColor(classR, classG, classB, 1)
        end
        if currentCharacterCard.topBg then
            currentCharacterCard.topBg:SetVertexColor(classR, classG, classB, CURRENT_CHARACTER_CARD_BG_ALPHA)
        end
        if currentCharacterCard.bottomBg then
            currentCharacterCard.bottomBg:SetVertexColor(0, 0, 0, 0.72)
        end
        if currentCharacterCard.dividerShadow then
            currentCharacterCard.dividerShadow:SetVertexColor(0, 0, 0, 0.40)
        end
        if currentCharacterCard.dividerLeft then
            ApplyHorizontalGradient(currentCharacterCard.dividerLeft, classR, classG, classB, 0, classR, classG, classB, CURRENT_CHARACTER_CARD_DIVIDER_ALPHA)
        end
        if currentCharacterCard.dividerRight then
            ApplyHorizontalGradient(currentCharacterCard.dividerRight, classR, classG, classB, CURRENT_CHARACTER_CARD_DIVIDER_ALPHA, classR, classG, classB, 0)
        end
        if currentCharacterCard.dividerSoftLeft then
            ApplyHorizontalGradient(currentCharacterCard.dividerSoftLeft, classR, classG, classB, 0, classR, classG, classB, CURRENT_CHARACTER_CARD_DIVIDER_ALPHA * 0.35)
        end
        if currentCharacterCard.dividerSoftRight then
            ApplyHorizontalGradient(currentCharacterCard.dividerSoftRight, classR, classG, classB, CURRENT_CHARACTER_CARD_DIVIDER_ALPHA * 0.35, classR, classG, classB, 0)
        end
        if currentCharacterCard.nameLabel then
            currentCharacterCard.nameLabel:SetText(GetPlainCurrentCharacterName(character, currentEntry.key))
            currentCharacterCard.nameLabel:SetTextColor(classR, classG, classB, 1)
        end
        if currentCharacterCard.ratingLabel then
            local score = tonumber(character.rating)
            if score and score > 0 then
                score = math.floor(score + 0.5)
                currentCharacterCard.ratingLabel:SetText(string.format("（%d）", score))
                if self.GetRaiderIOScoreColor then
                    local scoreR, scoreG, scoreB = self:GetRaiderIOScoreColor(score)
                    currentCharacterCard.ratingLabel:SetTextColor(scoreR, scoreG, scoreB, 1)
                else
                    currentCharacterCard.ratingLabel:SetTextColor(1, 1, 1, 0.95)
                end
                currentCharacterCard.ratingLabel:Show()
            else
                currentCharacterCard.ratingLabel:SetText("")
                currentCharacterCard.ratingLabel:Hide()
            end
        end
        if currentCharacterCard.armorLabel then
            currentCharacterCard.armorLabel:SetText(self:GetArmorType(character))
        end

        if currentCharacterCard.keyButton then
            currentCharacterCard.keyButton.keystoneLink = character.keystoneLink
            if currentCharacterCard.keyButton.roundedBackdrop and currentCharacterCard.keyButton.roundedBackdrop.SetBackdropBorderColor then
                currentCharacterCard.keyButton.roundedBackdrop:SetBackdropBorderColor(classR, classG, classB, 0.70)
            end
        end
        if currentCharacterCard.keyText then
            currentCharacterCard.keyText:SetText(GetCharacterCardKeystoneText(self, character))
            currentCharacterCard.keyText:SetTextColor(classR, classG, classB, 1)
        end

        if currentCharacterCard.teleportButton then
            local mapID = character.mapID
            local teleportMacroText = GetDungeonTeleportMacroText(mapID)
            local teleportStatus = GetDungeonTeleportStatus(mapID)

            currentCharacterCard.teleportButton.mapID = mapID
            SetSecureButtonAttributes(currentCharacterCard.teleportButton, "macrotext", teleportMacroText, "macrotext1", teleportMacroText)
            if currentCharacterCard.teleportButton.roundedBackdrop and currentCharacterCard.teleportButton.roundedBackdrop.SetBackdropBorderColor then
                currentCharacterCard.teleportButton.roundedBackdrop:SetBackdropBorderColor(classR, classG, classB, 0.70)
            end

            if currentCharacterCard.teleportIcon then
                if teleportStatus == nil then
                    currentCharacterCard.teleportIcon:SetVertexColor(1, 1, 1, 0.55)
                    currentCharacterCard.teleportIcon:SetAlpha(0.7)
                else
                    currentCharacterCard.teleportIcon:SetVertexColor(1, 1, 1, 0.92)
                    currentCharacterCard.teleportIcon:SetAlpha(1)
                end
            end
        end

        if currentCharacterCard.showInGroupToggle then
            currentCharacterCard.showInGroupToggle:SetChecked(true)
            if currentCharacterCard.showInGroupToggle.SetLocked then
                currentCharacterCard.showInGroupToggle:SetLocked(true)
            end
            currentCharacterCard.showInGroupToggle:Hide()
        end
        if currentCharacterCard.rolePromptLabel then
            currentCharacterCard.rolePromptLabel:SetText(TRANSLATE("ROLE.SELECTION"))
            currentCharacterCard.rolePromptLabel:SetTextColor(1, 1, 1, 0.96)
            currentCharacterCard.rolePromptLabel:Show()
        end
        if currentCharacterCard.roles then
            for _, roleKey in ipairs(self:GetRoleOrder()) do
                if currentCharacterCard.roles[roleKey] then
                    currentCharacterCard.roles[roleKey]:SetChecked(roles[roleKey] or false)
                end
            end
        end
    end

    local mythicStatsCard = container.mythicStatsCard or container.rightCard
    if mythicStatsCard then
        if mythicStatsCard.SetBackdropColor then
            mythicStatsCard:SetBackdropColor(MYTHIC_STATS_CARD_BG_COLOR.r, MYTHIC_STATS_CARD_BG_COLOR.g, MYTHIC_STATS_CARD_BG_COLOR.b, MYTHIC_STATS_CARD_BG_COLOR.a)
        end
        if mythicStatsCard.SetBackdropBorderColor then
            mythicStatsCard:SetBackdropBorderColor(MYTHIC_STATS_CARD_BORDER_COLOR.r, MYTHIC_STATS_CARD_BORDER_COLOR.g, MYTHIC_STATS_CARD_BORDER_COLOR.b, MYTHIC_STATS_CARD_BORDER_COLOR.a)
        end
        if mythicStatsCard.nameLabel then
            mythicStatsCard.nameLabel:SetText(TRANSLATE("CHARACTER.WEEKLY_REPORT.TITLE"))
            mythicStatsCard.nameLabel:SetTextColor(1, 0.90, 0.25, 1)
        end

        local weeklyReport = BuildCurrentCharacterWeeklyReport(self)
        mythicStatsCard.weeklyReport = weeklyReport

        if mythicStatsCard.weeklyTotalLabel then
            if weeklyReport.unavailable then
                mythicStatsCard.weeklyTotalLabel:SetText(WrapColor("-", WEEKLY_REPORT_RUN_LEVEL_COLORS.muted))
            else
                mythicStatsCard.weeklyTotalLabel:SetText(string.format(TRANSLATE("CHARACTER.WEEKLY_REPORT.CLEARED_COUNT_FORMAT"), weeklyReport.totalCount))
            end
        end

        if mythicStatsCard.weeklyVaultLabel then
            if weeklyReport.unavailable then
                mythicStatsCard.weeklyVaultLabel:SetText(TRANSLATE("CHARACTER.WEEKLY_REPORT.UNAVAILABLE"))
                mythicStatsCard.weeklyVaultLabel:SetTextColor(0.58, 0.58, 0.58, 0.95)
            else
                mythicStatsCard.weeklyVaultLabel:SetText(FormatWeeklyVaultRewardSummary(weeklyReport))
                mythicStatsCard.weeklyVaultLabel:SetTextColor(1, 1, 1, 0.95)
            end
        end

        if mythicStatsCard.weeklyTopRunsLabel then
            if weeklyReport.unavailable then
                mythicStatsCard.weeklyTopRunsLabel:SetText("")
            elseif weeklyReport.totalCount <= 0 then
                mythicStatsCard.weeklyTopRunsLabel:SetText(string.format("%s  %s", TRANSLATE("CHARACTER.WEEKLY_REPORT.TOP_RUNS"), WrapColor(TRANSLATE("CHARACTER.WEEKLY_REPORT.NO_RUNS"), WEEKLY_REPORT_RUN_LEVEL_COLORS.muted)))
            else
                local topRunParts = {}
                for _, run in ipairs(weeklyReport.topRuns or {}) do
                    topRunParts[#topRunParts + 1] = FormatWeeklyRunLevel(run)
                end
                mythicStatsCard.weeklyTopRunsLabel:SetText(string.format("%s  %s", TRANSLATE("CHARACTER.WEEKLY_REPORT.TOP_RUNS"), table.concat(topRunParts, "  ")))
            end
        end
    end
end

function MKA:CreateCharacterCardFrame(parentFrame)
    local card = CreateCurrentCharacterCard(parentFrame, nil, "currentCharacter")
    card:SetSize(CHARACTER_CARD_MIN_WIDTH, CURRENT_CHARACTER_CARD_HEIGHT)
    card:Hide()
    return card
end

function MKA:EnsureCharacterCards(requiredCount)
    local characterCards = self.widgets.characterCards
    local parentFrame = self.widgets.cardsContent
    if not characterCards or not parentFrame then
        return
    end

    while #characterCards < requiredCount do
        characterCards[#characterCards + 1] = self:CreateCharacterCardFrame(parentFrame)
    end

    for _, card in ipairs(characterCards) do
        if card:GetParent() ~= parentFrame then
            card:SetParent(parentFrame)
        end
    end
end

function MKA:LayoutCharacterCards(cardCount)
    local widgets = self.widgets
    local rowsContainer = widgets and widgets.cardsContent
    local characterCards = widgets and widgets.characterCards or {}
    if not rowsContainer then
        return
    end

    local availableWidth = rowsContainer:GetWidth() or 0
    if availableWidth <= 0 then
        availableWidth = (self.mainWindow and self.mainWindow:GetWidth() or 1000) - 70
    end

    local cardWidth = math.max(320, math.floor((availableWidth - CURRENT_CHARACTER_CARD_GAP) / 2))
    local rowCount = math.ceil(cardCount / 2)
    local cardsHeight = rowCount > 0 and (rowCount * CURRENT_CHARACTER_CARD_HEIGHT + math.max(0, rowCount - 1) * CHARACTER_CARD_GAP_Y) or 0
    local titleBottom = math.max(0, (tonumber(widgets.warbandCharacterCardsTopOffset) or CHARACTER_SECTION_TITLE_CARD_GAP) - CHARACTER_SECTION_TITLE_CARD_GAP)
    if widgets.warbandCharactersTitle and widgets.cardsContent then
        local playerTop = widgets.cardsContent:GetTop()
        local titleBottomScreen = widgets.warbandCharactersTitle:GetBottom()
        if playerTop and titleBottomScreen then
            titleBottom = math.max(0, playerTop - titleBottomScreen)
        end
    end
    local topOffset = titleBottom + CHARACTER_SECTION_TITLE_CARD_GAP
    local emptyHeight = widgets.emptyContainer and (widgets.emptyContainer:GetHeight() or 0) or 0
    local bottomGap = cardCount > 0 and WARBAND_CHARACTER_CARDS_BOTTOM_GAP or 0
    local totalHeight = topOffset + math.max(cardsHeight + bottomGap, emptyHeight)
    rowsContainer:SetHeight(math.max(totalHeight, 1))

    if widgets.emptyContainer then
        widgets.emptyContainer:ClearAllPoints()
        widgets.emptyContainer:SetPoint("TOPLEFT", rowsContainer, "TOPLEFT", 0, -topOffset)
        widgets.emptyContainer:SetPoint("TOPRIGHT", rowsContainer, "TOPRIGHT", -8, -topOffset)
    end

    for index = 1, cardCount do
        local card = characterCards[index]
        local column = (index - 1) % 2
        local row = math.floor((index - 1) / 2)
        local xOffset = column * (cardWidth + CURRENT_CHARACTER_CARD_GAP)
        local yOffset = -(topOffset + (row * (CURRENT_CHARACTER_CARD_HEIGHT + CHARACTER_CARD_GAP_Y)))

        card:ClearAllPoints()
        card:SetPoint("TOPLEFT", rowsContainer, "TOPLEFT", xOffset, yOffset)
        card:SetWidth(cardWidth)
        card:SetHeight(CURRENT_CHARACTER_CARD_HEIGHT)
    end

    UpdateCharacterCardsScrollState(widgets)
end

function MKA:SetCharacterCardVisible(card, visible)
    if not card then
        return
    end

    if visible then
        card:Show()
        return
    end

    card.characterKey = nil
    if card.nameLabel then
        card.nameLabel:SetText("")
    end
    if card.ratingLabel then
        card.ratingLabel:SetText("")
        card.ratingLabel:Hide()
    end
    if card.armorLabel then
        card.armorLabel:SetText("")
    end
    if card.keyButton then
        card.keyButton.keystoneLink = nil
    end
    if card.keyText then
        card.keyText:SetText("")
    end
    if card.teleportButton then
        card.teleportButton.mapID = nil
        SetSecureButtonAttributes(card.teleportButton, "macrotext", nil, "macrotext1", nil)
    end
    if card.teleportIcon then
        card.teleportIcon:SetVertexColor(1, 1, 1, 0.55)
        card.teleportIcon:SetAlpha(0.7)
    end
    if card.showInGroupToggle then
        card.showInGroupToggle:Show()
        if card.showInGroupToggle.SetLabelText then
            card.showInGroupToggle:SetLabelText(TRANSLATE("CHARACTER.JOIN_CARPOOL_LIST"))
        end
        card.showInGroupToggle:SetChecked(true)
        if card.showInGroupToggle.SetLocked then
            card.showInGroupToggle:SetLocked(false)
        end
    end
    if card.rolePromptLabel then
        card.rolePromptLabel:Hide()
    end
    if card.roles then
        for _, roleKey in ipairs(self:GetRoleOrder()) do
            card.roles[roleKey]:SetChecked(false)
        end
    end
    card:Hide()
end

function MKA:RefreshCharacterCard(card, characterEntry)
    local character = characterEntry.data
    local roles = self:NormalizeRoles(character)
    local classR, classG, classB = GetClassColorByToken(character.class)

    card.characterKey = characterEntry.key
    if card.SetBackdropColor then
        card:SetBackdropColor(0, 0, 0, 0)
    end
    if card.SetBackdropBorderColor then
        card:SetBackdropBorderColor(classR, classG, classB, 1)
    end
    if card.topBg then
        card.topBg:SetVertexColor(classR, classG, classB, CURRENT_CHARACTER_CARD_BG_ALPHA)
    end
    if card.bottomBg then
        card.bottomBg:SetVertexColor(0, 0, 0, 0.72)
    end
    if card.dividerShadow then
        card.dividerShadow:SetVertexColor(0, 0, 0, 0.40)
    end
    if card.dividerLeft then
        ApplyHorizontalGradient(card.dividerLeft, classR, classG, classB, 0, classR, classG, classB, CURRENT_CHARACTER_CARD_DIVIDER_ALPHA)
    end
    if card.dividerRight then
        ApplyHorizontalGradient(card.dividerRight, classR, classG, classB, CURRENT_CHARACTER_CARD_DIVIDER_ALPHA, classR, classG, classB, 0)
    end
    if card.dividerSoftLeft then
        ApplyHorizontalGradient(card.dividerSoftLeft, classR, classG, classB, 0, classR, classG, classB, CURRENT_CHARACTER_CARD_DIVIDER_ALPHA * 0.35)
    end
    if card.dividerSoftRight then
        ApplyHorizontalGradient(card.dividerSoftRight, classR, classG, classB, CURRENT_CHARACTER_CARD_DIVIDER_ALPHA * 0.35, classR, classG, classB, 0)
    end
    if card.nameLabel then
        card.nameLabel:SetText(GetPlainCurrentCharacterName(character, characterEntry.key))
        card.nameLabel:SetTextColor(classR, classG, classB, 1)
    end
    if card.ratingLabel then
        local score = tonumber(character.rating)
        if score and score > 0 then
            score = math.floor(score + 0.5)
            card.ratingLabel:SetText(string.format("（%d）", score))
            if self.GetRaiderIOScoreColor then
                local scoreR, scoreG, scoreB = self:GetRaiderIOScoreColor(score)
                card.ratingLabel:SetTextColor(scoreR, scoreG, scoreB, 1)
            else
                card.ratingLabel:SetTextColor(1, 1, 1, 0.95)
            end
            card.ratingLabel:Show()
        else
            card.ratingLabel:SetText("")
            card.ratingLabel:Hide()
        end
    end
    if card.armorLabel then
        card.armorLabel:SetText(self:GetArmorType(character))
    end
    if card.keyButton then
        card.keyButton.keystoneLink = character.keystoneLink
        if card.keyButton.roundedBackdrop and card.keyButton.roundedBackdrop.SetBackdropBorderColor then
            card.keyButton.roundedBackdrop:SetBackdropBorderColor(classR, classG, classB, 0.70)
        end
    end
    if card.keyText then
        card.keyText:SetText(GetCharacterCardKeystoneText(self, character))
        card.keyText:SetTextColor(classR, classG, classB, 1)
    end
    if card.teleportButton then
        local mapID = character.mapID
        local teleportMacroText = GetDungeonTeleportMacroText(mapID)
        local teleportStatus = GetDungeonTeleportStatus(mapID)

        card.teleportButton.mapID = mapID
        SetSecureButtonAttributes(card.teleportButton, "macrotext", teleportMacroText, "macrotext1", teleportMacroText)
        if card.teleportButton.roundedBackdrop and card.teleportButton.roundedBackdrop.SetBackdropBorderColor then
            card.teleportButton.roundedBackdrop:SetBackdropBorderColor(classR, classG, classB, 0.70)
        end

        if card.teleportIcon then
            if teleportStatus == nil then
                card.teleportIcon:SetVertexColor(1, 1, 1, 0.55)
                card.teleportIcon:SetAlpha(0.7)
            else
                card.teleportIcon:SetVertexColor(1, 1, 1, 0.92)
                card.teleportIcon:SetAlpha(1)
            end
        end
    end
    if card.showInGroupToggle then
        card.showInGroupToggle:Show()
        if card.showInGroupToggle.SetLabelText then
            card.showInGroupToggle:SetLabelText(TRANSLATE("CHARACTER.JOIN_CARPOOL_LIST"))
        end
        card.showInGroupToggle:SetChecked(self:IsCharacterShownInGroup(character))
        if card.showInGroupToggle.SetLocked then
            card.showInGroupToggle:SetLocked(false)
        end
    end
    if card.rolePromptLabel then
        card.rolePromptLabel:Hide()
    end

    if card.roles then
        for _, roleKey in ipairs(self:GetRoleOrder()) do
            if card.roles[roleKey] then
                card.roles[roleKey]:SetChecked(roles[roleKey] or false)
            end
        end
    end

    self:SetCharacterCardVisible(card, true)
end

function MKA:GetWarbandCharacterEntries()
    local currentKey = GetCurrentCharacterKey()
    local entries = {}
    local characters = self.db and self.db.global and self.db.global.characters or {}
    for characterKey, data in pairs(characters) do
        if characterKey ~= currentKey and IsWarbandMaxLevelCharacter(data) then
            entries[#entries + 1] = {
                key = characterKey,
                data = data,
            }
        end
    end
    for characterKey, data in pairs(self.testCharacters or {}) do
        if characterKey ~= currentKey and IsWarbandMaxLevelCharacter(data) then
            entries[#entries + 1] = {
                key = characterKey,
                data = data,
            }
        end
    end
    table.sort(entries, function(left, right)
        return ((left.data and left.data.name) or left.key) < ((right.data and right.data.name) or right.key)
    end)
    return entries
end

function CharacterTab.Build(owner, tabGroup)
    local createScrollContainer = core.CreateNativeScrollContainer
    local createSectionTitle = core.CreatePlayerInfoSectionTitle
    if not (createScrollContainer and createSectionTitle) then
        return
    end

    local playerInfo = CreateFrame("Frame", "PlayerInfo", UIParent)
    playerInfo.mkaFillDecorFrame = true
    playerInfo.mkaDecorFrameInsetLeft = 24
    playerInfo.mkaDecorFrameInsetTop = 4
    playerInfo.mkaDecorFrameInsetRight = 24
    playerInfo.mkaDecorFrameInsetBottom = 10
    tabGroup:AddChild(playerInfo)

    local scrollFrame, content, scrollBar = createScrollContainer(playerInfo, function()
        UpdateCharacterCardsScrollState(owner.widgets)
    end, 0)
    if scrollBar and tabGroup and tabGroup.frame then
        scrollBar:ClearAllPoints()
        scrollBar:SetPoint("TOPRIGHT", tabGroup.frame, "TOPRIGHT", 17, -4)
        scrollBar:SetPoint("BOTTOMRIGHT", tabGroup.frame, "BOTTOMRIGHT", 17, 4)
        scrollBar:SetWidth(17)
    end

    local playerInfoTitle, playerInfoTitleBg, playerInfoTitleText = createSectionTitle(content, "PlayerInfoTitle", TRANSLATE("CHARACTER.CURRENT"))
    playerInfoTitle:SetPoint("TOPLEFT", content, "TOPLEFT", 0, PLAYER_INFO_TITLE_OFFSET_Y)
    playerInfoTitle:SetPoint("TOPRIGHT", content, "TOPRIGHT", -PLAYER_INFO_TITLE_INSET_RIGHT, PLAYER_INFO_TITLE_OFFSET_Y)

    local currentCharacterCard = owner:CreateCurrentCharacterCardFrame(content, playerInfoTitle)

    local warbandCharactersTitle, warbandCharactersTitleBg, warbandCharactersTitleText = createSectionTitle(content, "PlayerWarbandCharactersTitle", TRANSLATE("WARBAND.CHARACTERS"))
    warbandCharactersTitle:SetPoint("TOPLEFT", currentCharacterCard, "BOTTOMLEFT", 0, -PLAYER_INFO_TITLE_TO_SECTION_GAP)
    warbandCharactersTitle:SetPoint("TOPRIGHT", currentCharacterCard, "BOTTOMRIGHT", -PLAYER_INFO_TITLE_INSET_RIGHT, -PLAYER_INFO_TITLE_TO_SECTION_GAP)

    local emptyContainer = CreateFrame("Frame", nil, content)
    emptyContainer:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -218)
    emptyContainer:SetPoint("TOPRIGHT", content, "TOPRIGHT", -8, 0)
    emptyContainer:SetHeight(40)

    local empty = emptyContainer:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    empty:SetPoint("TOPLEFT", emptyContainer, "TOPLEFT", 0, 0)
    empty:SetPoint("TOPRIGHT", emptyContainer, "TOPRIGHT", 0, 0)
    empty:SetJustifyH("LEFT")
    empty:SetJustifyV("TOP")
    empty:SetText(TRANSLATE("WARBAND.EMPTY.NO_MAX_LEVEL_CHARACTER"))
    if core.ApplyFont then
        core.ApplyFont(empty, 12, "")
    end
    emptyContainer:Hide()

    owner.widgets = {
        tabGroup = owner.widgets and owner.widgets.tabGroup or tabGroup,
        root = playerInfo,
        empty = empty,
        emptyContainer = emptyContainer,
        characterCardsContainer = content,
        playerInfo = playerInfo,
        playerInfoTitle = playerInfoTitle,
        playerInfoTitleBg = playerInfoTitleBg,
        playerInfoTitleText = playerInfoTitleText,
        warbandCharactersTitle = warbandCharactersTitle,
        warbandCharactersTitleBg = warbandCharactersTitleBg,
        warbandCharactersTitleText = warbandCharactersTitleText,
        warbandCharacterCardsTopOffset = 218,
        currentCharacterCard = currentCharacterCard,
        cardsScrollFrame = scrollFrame,
        cardsScrollBar = scrollBar,
        cardsContent = content,
        characterCards = {},
    }
    owner.tabViews = owner.tabViews or {}
    owner.tabViews.characters = owner.widgets

    if core.ApplyThemeToTabGroup then
        core.ApplyThemeToTabGroup(tabGroup)
    end

    if core.ApplyFontToFrame then
        core.ApplyFontToFrame(playerInfo)
    end

    C_Timer.After(0, function()
        if core.FinalizeFlatTabLayout then
            core.FinalizeFlatTabLayout(tabGroup)
        end
        if owner.widgets and owner.widgets.currentCharacterCard and owner.LayoutCurrentCharacterCardFrame then
            owner:LayoutCurrentCharacterCardFrame(owner.widgets.currentCharacterCard)
        end
    end)

    owner:RefreshMainWindow("BuildSelectedTab", "characters")
end
