local _, GF = ...

GF.ListTooltip = {}
local LT = GF.ListTooltip

local GOLD_R, GOLD_G, GOLD_B = 1, 0.82, 0

local TOOLTIP_ROLE_ICON_SIZE = GF.TOOLTIP_ROLE_ICON_SIZE or GF.ROLE_ICON_SIZE or 18
local TOOLTIP_LEADER_ICON_SIZE = TOOLTIP_ROLE_ICON_SIZE
local TOOLTIP_FACTION_ICON_SIZE = 14
local TOOLTIP_LEAVER_ICON_SIZE = GF.NON_ROLE_ICON_SIZE or 18
local TOOLTIP_BLACKLIST_ICON_SIZE = GF.NON_ROLE_ICON_SIZE or 18
local TOOLTIP_MEMBER_ICON_NAME_GAP = "  "
local TOOLTIP_LEADER_ATLAS = GF.TOOLTIP_LEADER_ICON_ATLAS or (GF.ROLE_ICON_ATLAS and GF.ROLE_ICON_ATLAS.LEADER) or "UI-LFG-RoleIcon-Leader"
local TOOLTIP_LEAVER_TEXTURE = "Interface\\AddOns\\GroupFinder\\Art\\UI\\Icon\\isLeaver.png"
local TOOLTIP_BLACKLIST_TEXTURE = "Interface\\AddOns\\GroupFinder\\Art\\UI\\Icon\\Blacklist.png"
local TOOLTIP_ROLE_ATLAS = GF.SEASON_DUNGEON_ROLE_ATLAS or GF.ROLE_ICON_ATLAS or {
	LEADER = "UI-LFG-RoleIcon-Leader",
	GUIDE = "UI-LFG-RoleIcon-Leader",
	TANK = "UI-LFG-RoleIcon-Tank",
	HEALER = "UI-LFG-RoleIcon-Healer",
	DAMAGER = "UI-LFG-RoleIcon-DPS",
	DPS = "UI-LFG-RoleIcon-DPS",
	NONE = "groupfinder-icon-emptyslot",
	DEFAULT = "groupfinder-icon-emptyslot",
}
local TOOLTIP_ROLE_PRIORITY = { TANK = 1, HEALER = 2, DAMAGER = 3 }
local TOOLTIP_FACTION_TEXTURES = {
	Alliance = "Interface\\FriendsFrame\\PlusManz-Alliance",
	Horde = "Interface\\FriendsFrame\\PlusManz-Horde",
}

local function getSearchResultInfo(resultID)
	if not resultID or not C_LFGList or not C_LFGList.GetSearchResultInfo then
		return nil
	end
	return C_LFGList.GetSearchResultInfo(resultID)
end

local function appendListingAge(tooltip, info)
	if not info or not info.age or info.age <= 0 or not SecondsToTime then
		return
	end
	local L = GF.L or {}
	local fmt = L.LIST_TIP_AGE_FMT or "时间：%s前"
	tooltip:AddLine(string.format(fmt, SecondsToTime(info.age, false, false, 1, false)))
end

local function colorText(text, color)
	text = tostring(text or "")
	if color and color.GenerateHexColorMarkup then
		return color:GenerateHexColorMarkup() .. text .. "|r"
	end
	if type(color) == "table" and color.colorStr then
		return "|c" .. color.colorStr .. text .. "|r"
	end
	if type(color) == "table" and color.r and color.g and color.b then
		return string.format(
			"|cff%02x%02x%02x%s|r",
			math.floor((color.r or 1) * 255 + 0.5),
			math.floor((color.g or 1) * 255 + 0.5),
			math.floor((color.b or 1) * 255 + 0.5),
			text
		)
	end
	return text
end

local function getColorRGB(color)
	if type(color) == "table" and color.GetRGB then
		local ok, r, g, b = pcall(color.GetRGB, color)
		if ok and r and g and b then
			return r, g, b
		end
	end
	if type(color) == "table" and color.GetRGBA then
		local ok, r, g, b = pcall(color.GetRGBA, color)
		if ok and r and g and b then
			return r, g, b
		end
	end
	if type(color) == "table" and color.r and color.g and color.b then
		return color.r, color.g, color.b
	end
end

local function makeTooltipColor(color, fallbackR, fallbackG, fallbackB)
	local r, g, b = getColorRGB(color)
	r, g, b = r or fallbackR or 1, g or fallbackG or 1, b or fallbackB or 1
	if CreateColor then
		return CreateColor(r, g, b, 1)
	end
	return {
		r = r,
		g = g,
		b = b,
		GetRGB = function(self)
			return self.r, self.g, self.b
		end,
	}
end

local function getTooltipFontString(tooltip, suffix, lineIndex)
	if not tooltip or not suffix or not lineIndex then
		return nil
	end
	local direct = tooltip[suffix .. lineIndex]
	if direct then
		return direct
	end
	local name = tooltip.GetName and tooltip:GetName()
	if name and name ~= "" then
		return _G[name .. suffix .. lineIndex]
	end
end

local function restoreLeaderScoreLines(tooltip)
	local lines = tooltip and tooltip._gfLeaderScoreTooltipLines
	if not lines then
		return
	end
	for lineIndex, line in pairs(lines) do
		local fs = getTooltipFontString(tooltip, "TextRight", lineIndex)
		local r, g, b = getColorRGB(line.color)
		if fs then
			if line.text and fs.SetText then
				fs:SetText(line.text)
			end
			if r and fs.SetTextColor then
				fs:SetTextColor(r, g, b, 1)
			end
		end
	end
end

local function addColoredDoubleLine(tooltip, label, value, rightColor)
	if not tooltip or not label or value == nil then
		return
	end
	local scoreColor = makeTooltipColor(rightColor, 1, 1, 1)
	if GameTooltip_AddColoredDoubleLine then
		GameTooltip_AddColoredDoubleLine(
			tooltip,
			tostring(label),
			tostring(value),
			makeTooltipColor(nil, GOLD_R, GOLD_G, GOLD_B),
			scoreColor,
			false
		)
		return
	end
	local r, g, b = getColorRGB(scoreColor)
	tooltip:AddDoubleLine(tostring(label), tostring(value), GOLD_R, GOLD_G, GOLD_B, r or 1, g or 1, b or 1)
end

local function addLeaderScoreLine(tooltip, label, scoreText, scoreColor)
	local displayText = colorText(scoreText, scoreColor)
	addColoredDoubleLine(tooltip, label, displayText, scoreColor)
	local lineIndex = tooltip.NumLines and tooltip:NumLines()
	if lineIndex then
		tooltip._gfLeaderScoreTooltipLines = tooltip._gfLeaderScoreTooltipLines or {}
		tooltip._gfLeaderScoreTooltipLines[lineIndex] = {
			text = displayText,
			color = scoreColor,
		}
	end
end

local function getClassColor(classFilename)
	local colors = CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS
	return classFilename and colors and colors[classFilename] or nil
end

local function addGoldDoubleLine(tooltip, label, value)
	if not tooltip or not label or value == nil then
		return
	end
	tooltip:AddDoubleLine(label, tostring(value), GOLD_R, GOLD_G, GOLD_B, 1, 1, 1)
end

local function getInlineAtlas(atlasName, size)
	if type(atlasName) ~= "string" or atlasName == "" then
		return nil
	end
	size = size or 16
	return string.format("|A:%s:%d:%d|a", atlasName, size, size)
end

local function getInlineTexture(texturePath, size)
	if type(texturePath) ~= "string" or texturePath == "" then
		return nil
	end
	size = size or 16
	return string.format("|T%s:%d:%d|t", texturePath, size, size)
end

local function normalizeTooltipRole(role)
	if type(role) ~= "string" then
		return nil
	end
	role = role:upper()
	if role == "HEAL" then
		return "HEALER"
	end
	if role == "DPS" then
		return "DAMAGER"
	end
	return role
end

local function getRoleIconMarkup(role)
	role = normalizeTooltipRole(role)
	local atlas = role and TOOLTIP_ROLE_ATLAS[role]
	return getInlineAtlas(atlas, TOOLTIP_ROLE_ICON_SIZE)
end

local function getMemberRolePriority(member)
	local role = normalizeTooltipRole(member and (member.assignedRole or member.role))
	return TOOLTIP_ROLE_PRIORITY[role] or 99
end

local function sortMembersByRole(members)
	local sorted = {}
	for index, member in ipairs(members or {}) do
		sorted[#sorted + 1] = {
			index = index,
			member = member,
		}
	end
	table.sort(sorted, function(a, b)
		local aPriority = getMemberRolePriority(a.member)
		local bPriority = getMemberRolePriority(b.member)
		if aPriority ~= bPriority then
			return aPriority < bPriority
		end
		return a.index < b.index
	end)
	for index, item in ipairs(sorted) do
		members[index] = item.member
	end
	return members
end

local function getLeaderIconMarkup()
	return getInlineAtlas(TOOLTIP_LEADER_ATLAS, TOOLTIP_LEADER_ICON_SIZE) or ""
end

local function getLeaverIconMarkup()
	return getInlineTexture(TOOLTIP_LEAVER_TEXTURE, TOOLTIP_LEAVER_ICON_SIZE) or ""
end

local function getBlacklistIconMarkup()
	return getInlineTexture(TOOLTIP_BLACKLIST_TEXTURE, TOOLTIP_BLACKLIST_ICON_SIZE) or ""
end

local function findBlockedMemberRow(member)
	if not member or type(member.name) ~= "string" or member.name == "" then
		return nil
	end
	local bl = GF.Blocklist
	if not bl or not bl.IsEnabled or not bl:IsEnabled() or not bl.FindPlayerMatch then
		return nil
	end
	return bl:FindPlayerMatch(member.name)
end

local function isUnknownMemberName(name)
	name = strtrim and strtrim(tostring(name or "")) or tostring(name or "")
	if name == "" or name == "-" or name == "未知目标" or name == "Unknown" or name == "Unknown Target" then
		return true
	end
	if _G.UNKNOWNOBJECT and name == _G.UNKNOWNOBJECT then
		return true
	end
	if _G.UNKNOWN and name == _G.UNKNOWN then
		return true
	end
	if _G.UNKNOWNBEING and name == _G.UNKNOWNBEING then
		return true
	end
	return false
end

local function getDisplayName(name)
	if type(name) ~= "string" or name == "" then
		return "-"
	end
	if Ambiguate then
		return Ambiguate(name, "short")
	end
	return name:gsub("%-.+$", "")
end

local function splitMemberName(name)
	if type(name) ~= "string" or name == "" then
		return nil, nil
	end
	local shortName, realm = name:match("^([^-]+)%-(.+)$")
	if shortName and realm then
		return shortName, realm
	end
	return name, nil
end

local function isLeaderMember(info, member)
	if not member then
		return false
	end
	if member.isLeader == true then
		return true
	end
	local leaderName = info and info.leaderName
	if type(leaderName) ~= "string" or leaderName == "" then
		return false
	end
	if member.name == leaderName then
		return true
	end
	local leaderShortName = leaderName:match("^([^-]+)%-.+$") or leaderName
	local memberShortName = splitMemberName(member.name)
	return leaderShortName and memberShortName and memberShortName == leaderShortName
end

local function safeGetMemberCounts(resultID)
	if not resultID or not C_LFGList or not C_LFGList.GetSearchResultMemberCounts then
		return nil
	end
	local ok, counts = pcall(C_LFGList.GetSearchResultMemberCounts, resultID)
	return ok and type(counts) == "table" and counts or nil
end

local function fetchMembersForTooltip(resultID, info)
	local members = {}
	local leaderClassFilename
	local hasLeaver = false
	local blockedMembers = {}
	local numMembers = tonumber(info and info.numMembers) or 0
	if not (C_LFGList and C_LFGList.GetSearchResultPlayerInfo) then
		return members, nil, false, blockedMembers
	end
	for i = 1, numMembers do
		local ok, playerInfo = pcall(C_LFGList.GetSearchResultPlayerInfo, resultID, i)
		if ok and type(playerInfo) == "table" then
			playerInfo.displayName = playerInfo.displayName or getDisplayName(playerInfo.name)
			playerInfo.assignedRole = playerInfo.assignedRole or playerInfo.role
			playerInfo.classFilename = playerInfo.classFilename or playerInfo.classFileName or playerInfo.classFile
			if playerInfo.isLeaver then
				hasLeaver = true
			end
			if isLeaderMember(info, playerInfo) then
				leaderClassFilename = playerInfo.classFilename
			end
			local blockedRow = findBlockedMemberRow(playerInfo)
			if blockedRow then
				playerInfo._gfBlockedRow = blockedRow
				blockedMembers[#blockedMembers + 1] = playerInfo
			end
			members[#members + 1] = playerInfo
		end
	end
	sortMembersByRole(members)
	sortMembersByRole(blockedMembers)
	return members, leaderClassFilename, hasLeaver, blockedMembers
end

local function getAvailableSlots(info, activityInfo, counts)
	local maxMembers = tonumber(info and info.maxMembers)
	if not maxMembers or maxMembers <= 0 then
		maxMembers = tonumber(activityInfo and activityInfo.maxNumPlayers) or 5
	end
	if maxMembers <= 0 then
		maxMembers = 5
	end
	local total = (tonumber(counts and counts.TANK) or 0)
		+ (tonumber(counts and counts.HEALER) or 0)
		+ (tonumber(counts and counts.DAMAGER) or 0)
	if total <= 0 then
		total = tonumber(info and info.numMembers) or 0
	end
	return math.max(0, maxMembers - total), total, maxMembers
end

local function getScoreTextAndColor(info, activityInfo)
	local delistedColor = info and info.isDelisted and (LFG_LIST_DELISTED_FONT_COLOR or GRAY) or nil
	local text, color = GF.Result and GF.Result.GetBrowseScoreDisplay
		and GF.Result:GetBrowseScoreDisplay(info, activityInfo, delistedColor)
	if text then
		if color then
			return text, color
		end
		if GF.Result and GF.Result.GetDungeonScoreColor then
			return text, GF.Result:GetDungeonScoreColor(text, delistedColor)
		end
		return text, delistedColor or HIGHLIGHT_FONT_COLOR
	end
	local raw = tonumber(info and info.leaderOverallDungeonScore)
	if raw then
		local fallbackColor = delistedColor
		if not fallbackColor and GF.Result and GF.Result.GetDungeonScoreColor then
			fallbackColor = GF.Result:GetDungeonScoreColor(raw)
		end
		return tostring(math.floor(raw + 0.5)), fallbackColor or HIGHLIGHT_FONT_COLOR
	end
	return nil, nil
end

local function makeRunLevelWithIncrement(dungeonScoreInfo)
	if type(dungeonScoreInfo) ~= "table" or dungeonScoreInfo.bestRunLevel == nil then
		return nil
	end
	local plus = GROUPFINDER_PLUS or "+"
	local pluses = ""
	for _ = 1, math.max(0, tonumber(dungeonScoreInfo.bestLevelIncrement) or 0) do
		pluses = pluses .. plus
	end
	return pluses .. colorText(dungeonScoreInfo.bestRunLevel, HIGHLIGHT_FONT_COLOR)
end

local function formatDungeonScoreInfo(dungeonScoreInfo, scoreColor)
	local levelText = makeRunLevelWithIncrement(dungeonScoreInfo)
	if not levelText then
		return nil
	end
	local mapName = dungeonScoreInfo.mapName
	if type(mapName) == "string" and mapName ~= "" then
		return levelText .. " " .. colorText(mapName, scoreColor)
	end
	return levelText
end

local function getRoleLabels()
	local L = GF.L or {}
	return {
		TANK = L.LIST_TIP_ROLE_TANK or TANK or "Tank",
		HEALER = L.LIST_TIP_ROLE_HEALER or HEALER or "Healer",
		DAMAGER = L.LIST_TIP_ROLE_DPS or DPS or "DPS",
	}
end

local function getLeaderFactionIconMarkup(info)
	if not info or info.leaderFactionGroup == nil then
		return nil
	end
	if not PLAYER_FACTION_GROUP or not UnitFactionGroup then
		return nil
	end
	local factionKey = PLAYER_FACTION_GROUP[info.leaderFactionGroup]
	if not factionKey or UnitFactionGroup("player") == factionKey then
		return nil
	end
	local texture = TOOLTIP_FACTION_TEXTURES[factionKey]
	return texture and getInlineTexture(texture, TOOLTIP_FACTION_ICON_SIZE)
end

local function colorizeLeaderName(leaderName, classFilename)
	if not leaderName then
		return nil
	end
	return colorText(leaderName, getClassColor(classFilename))
end

local function formatLeaderNameWithFaction(info, leaderName, classFilename)
	local text = colorizeLeaderName(leaderName, classFilename)
	local factionIcon = getLeaderFactionIconMarkup(info)
	if factionIcon and factionIcon ~= "" then
		text = (text or tostring(leaderName or "-")) .. " " .. factionIcon
	end
	return text
end

local appendMyKeyStoneHeader
local appendMyKeyStoneMembers
local appendLeaverMembers
local appendBlacklistMembers

local function isPvpTooltipActivity(info, activityInfo)
	if activityInfo and (activityInfo.isPvpActivity or activityInfo.isRatedPvpActivity) then
		return true
	end
	local ratings = info and info.leaderPvpRatingInfo
	return type(ratings) == "table" and ratings[1] ~= nil
end

local function appendPvpLeaderLine(tooltip, info, leaderClassFilename)
	local L = GF.L or {}
	local leaderText = (info and info.leaderName and info.leaderName ~= "" and info.leaderName) or "-"
	local prefix = L.LIST_TIP_LEADER_PREFIX or "队长："
	addGoldDoubleLine(tooltip, prefix, formatLeaderNameWithFaction(info, leaderText, leaderClassFilename))
end

local function appendPvpRatingLine(tooltip, info, activityInfo)
	local ratingInfo, tierName
	if GF.Result and GF.Result.GetLeaderPvpRatingInfo then
		ratingInfo, tierName = GF.Result:GetLeaderPvpRatingInfo(info, activityInfo)
	end
	if not ratingInfo or not ratingInfo.rating then
		return
	end
	local L = GF.L or {}
	local activityName = ratingInfo.activityName
		or (activityInfo and (activityInfo.fullName or activityInfo.shortName or activityInfo.name))
		or L.PVP_RATING
		or "PVP"
	if activityName == "" then
		activityName = L.PVP_RATING or "PVP"
	end
	local ratingText = tostring(ratingInfo.rating)
	if tierName and tierName ~= "" then
		ratingText = string.format(L.LIST_TIP_PVP_RATING_TIER_FMT or "%s（%s）", ratingText, tierName)
	end
	local label = string.format(L.LIST_TIP_PVP_ACTIVITY_PREFIX_FMT or "%s：", activityName)
	addGoldDoubleLine(tooltip, label, colorText(ratingText, HIGHLIGHT_FONT_COLOR))
end

local function appendPvpMemberSummary(tooltip, resultID, info, activityInfo)
	local L = GF.L or {}
	local counts = safeGetMemberCounts(resultID) or {}
	local slots, totalMembers = getAvailableSlots(info, activityInfo, counts)
	addGoldDoubleLine(
		tooltip,
		L.LIST_TIP_MEMBERS_PREFIX or "队员：",
		string.format(
			"%d (%d/%d/%d)",
			totalMembers or tonumber(info and info.numMembers) or 0,
			tonumber(counts.TANK) or 0,
			tonumber(counts.HEALER) or 0,
			tonumber(counts.DAMAGER) or 0
		)
	)
	addGoldDoubleLine(tooltip, L.LIST_TIP_SLOTS_PREFIX or "空位：", slots or 0)
end

local function appendPvpCreatedLine(tooltip, info)
	local age = tonumber(info and info.age)
	if not age then
		return
	end
	local L = GF.L or {}
	local fmt = L.LIST_TIP_MINUTES_AGO_FMT or "%d 分钟前"
	local prefix = L.LIST_TIP_CREATED_PREFIX or "创建于："
	addGoldDoubleLine(tooltip, prefix, string.format(fmt, math.max(0, math.floor(age / 60))))
end

local function showPvpTooltip(tooltip, resultID, info, activityInfo, leaderClassFilename, members, hasLeaver, blockedMembers)
	tooltip:ClearLines()
	tooltip._gfLeaderScoreTooltipLines = nil
	appendMyKeyStoneHeader(tooltip, info, activityInfo)
	appendPvpLeaderLine(tooltip, info, leaderClassFilename)
	appendPvpRatingLine(tooltip, info, activityInfo)
	appendPvpMemberSummary(tooltip, resultID, info, activityInfo)
	appendPvpCreatedLine(tooltip, info)
	appendMyKeyStoneMembers(tooltip, info, members or {})
	appendBlacklistMembers(tooltip, blockedMembers)
	appendLeaverMembers(tooltip, members, hasLeaver)
	if info and info.isDelisted and LFG_LIST_ENTRY_DELISTED then
		tooltip:AddLine(" ")
		tooltip:AddLine(LFG_LIST_ENTRY_DELISTED, 1, 0.1, 0.1, true)
	end
	if GF.Font then
		GF.Font.ApplyTooltipFont(tooltip)
	end
	tooltip:Show()
end

local function appendCompletedEncounters(tooltip, resultID)
	if not C_LFGList or not C_LFGList.GetSearchResultEncounterInfo then
		return
	end
	local completedEncounters = C_LFGList.GetSearchResultEncounterInfo(resultID)
	if not completedEncounters or #completedEncounters == 0 then
		return
	end
	tooltip:AddLine(" ")
	tooltip:AddLine(LFG_LIST_BOSSES_DEFEATED)
	local r, g, b = 1, 0.1, 0.1
	if RED_FONT_COLOR then
		r, g, b = RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b
	end
	for i = 1, #completedEncounters do
		tooltip:AddLine(completedEncounters[i], r, g, b)
	end
end

local function hasSocialMembers(info)
	if not info then
		return false
	end
	return (info.numBNetFriends or 0) + (info.numCharFriends or 0) + (info.numGuildMates or 0) > 0
end

local function formatSearchResultFriendList(resultID)
	if not C_LFGList or not C_LFGList.GetSearchResultFriends then
		return nil
	end
	local bNetFriends, charFriends, guildMates = C_LFGList.GetSearchResultFriends(resultID)
	local list = ""
	local displayedFirst = false
	local function appendNames(names, colorCode)
		for i = 1, #(names or {}) do
			if displayedFirst then
				list = list .. (PLAYER_LIST_DELIMITER or ", ")
			else
				displayedFirst = true
			end
			list = list .. (colorCode or "") .. names[i] .. (FONT_COLOR_CODE_CLOSE or "|r")
		end
	end
	appendNames(bNetFriends, FRIENDS_BNET_NAME_COLOR_CODE)
	appendNames(charFriends, FRIENDS_WOW_NAME_COLOR_CODE)
	local guildColor
	if ChatTypeInfo and ChatTypeInfo.GUILD and RGBTableToColorCode then
		guildColor = RGBTableToColorCode(ChatTypeInfo.GUILD)
	end
	appendNames(guildMates, guildColor)
	if list == "" then
		return nil
	end
	return list
end

local function appendFriendsInGroup(tooltip, resultID, info)
	if not hasSocialMembers(info) or not LFG_LIST_TOOLTIP_FRIENDS_IN_GROUP then
		return
	end
	local friendList = formatSearchResultFriendList(resultID)
	if not friendList then
		return
	end
	tooltip:AddLine(" ")
	tooltip:AddLine(LFG_LIST_TOOLTIP_FRIENDS_IN_GROUP)
	tooltip:AddLine(friendList, 1, 1, 1, true)
end

local function getLeaverLineText(member)
	local memberName = member and (member.displayName or getDisplayName(member.name) or member.name)
	if isUnknownMemberName(memberName) then
		return nil
	end
	local L = GF.L or {}
	local icon = getLeaverIconMarkup()
	local prefix = L.LIST_TIP_LEAVER_PREFIX or L.TYPE_LEAVER or "逃兵："
	local classColor = getClassColor(member.classFilename) or HIGHLIGHT_FONT_COLOR
	return string.format(
		"%s%s%s",
		icon ~= "" and (icon .. " ") or "",
		colorText(prefix, RED_FONT_COLOR or { r = 1, g = 0.1, b = 0.1 }),
		colorText(memberName, classColor)
	)
end

local function getBlacklistLineText(member)
	local memberName = member and (member.displayName or getDisplayName(member.name) or member.name)
	if isUnknownMemberName(memberName) then
		return nil
	end
	local L = GF.L or {}
	local icon = getBlacklistIconMarkup()
	local prefix = L.LIST_TIP_BLACKLIST_PREFIX or L.TYPE_BLACKLIST or "黑名单："
	local classColor = getClassColor(member.classFilename) or HIGHLIGHT_FONT_COLOR
	return string.format(
		"%s%s%s",
		icon ~= "" and (icon .. " ") or "",
		colorText(prefix, RED_FONT_COLOR or { r = 1, g = 0.1, b = 0.1 }),
		colorText(memberName, classColor)
	)
end

function appendBlacklistMembers(tooltip, members)
	if not members or #members == 0 then
		return
	end
	local addedSpacer = false
	for _, member in ipairs(members) do
		local lineText = getBlacklistLineText(member)
		if lineText then
			if not addedSpacer then
				tooltip:AddLine(" ")
				addedSpacer = true
			end
			tooltip:AddLine(lineText, 1, 1, 1, true)
		end
	end
end

function appendLeaverMembers(tooltip, members, hasLeaver)
	if not hasLeaver or not members then
		return
	end
	local addedSpacer = false
	for _, member in ipairs(members) do
		if member and member.isLeaver then
			local lineText = getLeaverLineText(member)
			if lineText then
				if not addedSpacer then
					tooltip:AddLine(" ")
					addedSpacer = true
				end
				tooltip:AddLine(lineText, 1, 1, 1, true)
			end
		end
	end
end

function appendMyKeyStoneHeader(tooltip, info, activityInfo)
	local title = info and info.name
	tooltip:AddLine((title and title ~= "" and title) or "-", 1, 1, 1, true)
	local activityName = activityInfo and (activityInfo.fullName or activityInfo.shortName or activityInfo.name)
	if activityName and activityName ~= "" then
		tooltip:AddLine(activityName, 0.2, 1, 0.2, true)
	end
	tooltip:AddLine(" ")
end

local function appendMyKeyStoneDetails(tooltip, resultID, info, activityInfo, leaderClassFilename)
	local L = GF.L or {}
	local counts = safeGetMemberCounts(resultID) or {}
	local slots, totalMembers = getAvailableSlots(info, activityInfo, counts)
	local leaderText = (info and info.leaderName and info.leaderName ~= "" and info.leaderName) or "-"
	addGoldDoubleLine(tooltip, L.LIST_TIP_LEADER_PREFIX or "队长：", formatLeaderNameWithFaction(info, leaderText, leaderClassFilename))

	local scoreText, scoreColor = getScoreTextAndColor(info, activityInfo)
	if scoreText then
		addLeaderScoreLine(
			tooltip,
			L.LIST_TIP_LEADER_SCORE_PREFIX or "队长大秘评分：",
			scoreText,
			scoreColor
		)
	end

	addGoldDoubleLine(
		tooltip,
		L.LIST_TIP_MEMBERS_PREFIX or "队员：",
		string.format(
			"%d (%d/%d/%d)",
			totalMembers or tonumber(info and info.numMembers) or 0,
			tonumber(counts.TANK) or 0,
			tonumber(counts.HEALER) or 0,
			tonumber(counts.DAMAGER) or 0
		)
	)
	addGoldDoubleLine(tooltip, L.LIST_TIP_SLOTS_PREFIX or "空位：", slots or 0)

	local requiredItemLevel = tonumber(info and info.requiredItemLevel)
	if requiredItemLevel and requiredItemLevel > 0 then
		addGoldDoubleLine(
			tooltip,
			L.LIST_TIP_REQUIRED_ILVL_PREFIX or "需要物品等级：",
			tostring(math.floor(requiredItemLevel + 0.5))
		)
	end

	local age = tonumber(info and info.age)
	if age then
		local fmt = L.LIST_TIP_MINUTES_AGO_FMT or "%d 分钟前"
		addGoldDoubleLine(
			tooltip,
			L.LIST_TIP_CREATED_PREFIX or "创建于：",
			string.format(fmt, math.max(0, math.floor(age / 60)))
		)
	end
end

local function appendMyKeyStoneBestRuns(tooltip, info, activityInfo)
	if not activityInfo or not activityInfo.isMythicPlusActivity then
		return
	end
	local bestDungeonScoreInfo = info and info.leaderDungeonScoreInfo and info.leaderDungeonScoreInfo[1]
	local _, scoreColor = getScoreTextAndColor(info, activityInfo)
	local bestDungeonText = formatDungeonScoreInfo(bestDungeonScoreInfo, scoreColor)
	local bestRunText = formatDungeonScoreInfo(info and info.leaderBestDungeonScoreInfo, scoreColor)
	if not bestDungeonText and not bestRunText then
		return
	end

	local L = GF.L or {}
	tooltip:AddLine(" ")
	if bestDungeonText then
		addGoldDoubleLine(tooltip, L.LIST_TIP_BEST_DUNGEON_PREFIX or "最佳副本：", bestDungeonText)
	end
	if bestRunText then
		addGoldDoubleLine(tooltip, L.LIST_TIP_BEST_RUN_PREFIX or "最佳成绩：", bestRunText)
	end
end

function appendMyKeyStoneMembers(tooltip, info, members)
	local L = GF.L or {}
	if #members > 0 then
		tooltip:AddLine(" ")
		tooltip:AddLine(L.LIST_TIP_MEMBERS_HEADER or "队伍成员", GOLD_R, GOLD_G, GOLD_B)
		local shownMembers = 0
		local leaderIcon = getLeaderIconMarkup()
		for _, member in ipairs(members) do
			local memberName = member.displayName or member.name
			if not isUnknownMemberName(memberName) then
				local role = normalizeTooltipRole(member.assignedRole)
				local rolePrefix = getRoleIconMarkup(role)
					or (getRoleLabels()[role] or role or "?")
				local classColor = getClassColor(member.classFilename)
				local formattedName = colorText(memberName, classColor)
				if isLeaderMember(info, member) and leaderIcon ~= "" then
					formattedName = formattedName .. TOOLTIP_MEMBER_ICON_NAME_GAP .. leaderIcon
				end
				local leftText = rolePrefix .. TOOLTIP_MEMBER_ICON_NAME_GAP .. formattedName
				if member.specName and member.specName ~= "" then
					local r, g, b = getColorRGB(classColor)
					tooltip:AddDoubleLine(leftText, member.specName, 1, 1, 1, r or 1, g or 1, b or 1)
				else
					tooltip:AddLine(leftText, 1, 1, 1, true)
				end
				shownMembers = shownMembers + 1
			end
		end
		if shownMembers <= 0 then
			tooltip:AddLine(L.LIST_TIP_MEMBERS_LOADING or "成员信息加载中", 0.7, 0.7, 0.7, true)
		end
	elseif tonumber(info and info.numMembers) and (tonumber(info.numMembers) or 0) > 0 then
		tooltip:AddLine(" ")
		tooltip:AddLine(L.LIST_TIP_MEMBERS_LOADING or "成员信息加载中", 0.7, 0.7, 0.7, true)
	end
end

local function appendMyKeyStoneComment(tooltip, info, resultID)
	local comment = GF.Result and GF.Result.GetListingComment and GF.Result:GetListingComment(info, resultID) or ""
	if comment == "" then
		return
	end
	local c = GREEN_FONT_COLOR or LFG_LIST_COMMENT_FONT_COLOR or { r = 0.2, g = 1, b = 0.2 }
	tooltip:AddLine(" ")
	tooltip:AddLine(comment, c.r, c.g, c.b, true)
end

function LT:ShowMyKeyStoneStyle(tooltip, resultID)
	local info = getSearchResultInfo(resultID)
	if not info or not info.activityIDs or not info.activityIDs[1] then
		return
	end
	if GF.Result and GF.Result.ShouldHideUnavailableResult and GF.Result:ShouldHideUnavailableResult(resultID, info) then
		if GF.FindGroupTab and GF.FindGroupTab.DropFrozenResult then
			if GF.FindGroupTab:DropFrozenResult(resultID) then
				GF.FindGroupTab:RefreshList({ preserveScroll = true })
			end
		end
		return
	end
	local shouldRefreshRow = false
	if GF.Result and GF.Result.IsUnreadableLfgText then
		local cached = GF.Result.entryCache and GF.Result.entryCache[resultID]
		local cachedInfo = cached and cached.info
		shouldRefreshRow = cachedInfo
			and (
				(GF.Result:IsUnreadableLfgText(cachedInfo.name) and not GF.Result:IsUnreadableLfgText(info.name))
				or (GF.Result:IsUnreadableLfgText(cachedInfo.comment) and not GF.Result:IsUnreadableLfgText(info.comment))
			)
	end
	if GF.Result and GF.Result.RefreshEntryInfo then
		local entry = GF.Result:RefreshEntryInfo(resultID, info)
		if entry and entry.info then
			info = entry.info
		elseif GF.FindGroupTab and GF.FindGroupTab.DropFrozenResult then
			if GF.FindGroupTab:DropFrozenResult(resultID) then
				GF.FindGroupTab:RefreshList({ preserveScroll = true })
			end
			return
		end
	end
	if shouldRefreshRow and GF.FindGroupTab and GF.FindGroupTab.UpdateRowByResultID then
		GF.FindGroupTab:UpdateRowByResultID(resultID)
	end
	local activityInfo = C_LFGList.GetActivityInfoTable(info.activityIDs[1], nil, info.isWarMode)
	local members, leaderClassFilename, hasLeaver, blockedMembers = fetchMembersForTooltip(resultID, info)

	if isPvpTooltipActivity(info, activityInfo) then
		showPvpTooltip(tooltip, resultID, info, activityInfo, leaderClassFilename, members, hasLeaver, blockedMembers)
		return
	end

	tooltip:ClearLines()
	tooltip._gfLeaderScoreTooltipLines = nil
	appendMyKeyStoneHeader(tooltip, info, activityInfo)
	appendMyKeyStoneDetails(tooltip, resultID, info, activityInfo, leaderClassFilename)
	appendMyKeyStoneBestRuns(tooltip, info, activityInfo)
	appendMyKeyStoneMembers(tooltip, info, members)
	appendBlacklistMembers(tooltip, blockedMembers)
	appendLeaverMembers(tooltip, members, hasLeaver)
	appendCompletedEncounters(tooltip, resultID)
	appendFriendsInGroup(tooltip, resultID, info)
	if info.isDelisted and LFG_LIST_ENTRY_DELISTED then
		tooltip:AddLine(" ")
		tooltip:AddLine(LFG_LIST_ENTRY_DELISTED, 1, 0.1, 0.1, true)
	end
	appendMyKeyStoneComment(tooltip, info, resultID)

	if GF.Font then
		GF.Font.ApplyTooltipFont(tooltip)
	end
	tooltip:Show()
	restoreLeaderScoreLines(tooltip)
end

function LT:ShowFull(tooltip, resultID)
	self:ShowMyKeyStoneStyle(tooltip, resultID)
end

function LT:Show(tooltip, resultID, owner)
	if not tooltip or not resultID or not owner then
		return
	end
	tooltip:SetOwner(owner, "ANCHOR_RIGHT", 25, 0)
	if GF.Font and GF.Font.BeginTooltipFont then
		GF.Font.BeginTooltipFont(tooltip)
	end
	self:ShowFull(tooltip, resultID)
end
