local _, GF = ...

GF.RowContextMenu = {}
local RCM = GF.RowContextMenu

local ROW_CTX_CENSORED_TITLE_COLOR = { 1, 0.1, 0.1, 1 }
local ROW_CTX_SIGN_UP_COLOR = { 0, 1, 0, 1 }
local ROW_CTX_REPORT_AD_COLOR = { 1, 0.82, 0, 1 }
local ROW_CTX_BLOCK_LEADER_COLOR = { 1, 0.1, 0.1, 1 }
local ROW_CTX_TITLE_DIVIDER_ATLAS = "levelup-bar-gold"
local ROW_CTX_TITLE_DIVIDER_COLOR = { 1, 0.82, 0 }
local ROW_CTX_TITLE_DIVIDER_ALPHA = 0.62
local ROW_CTX_TITLE_DIVIDER_SLOT_HEIGHT = 10
local ROW_CTX_TITLE_DIVIDER_VISUAL_OFFSET_Y = 2
local ROW_CTX_TITLE_DIVIDER_ACTION_GAP = 2

local function closeMenu()
	local manager = Menu and type(Menu.GetManager) == "function"
		and Menu.GetManager() or nil
	if manager and type(manager.CloseMenus) == "function" then
		manager:CloseMenus()
		return
	end
	if type(CloseMenus) == "function" then
		CloseMenus()
	end
end

local function getRowDisplayTitle(row)
	local text = row and row.title and row.title.GetText and row.title:GetText()
	if text and text ~= "" then
		return text
	end
	text = row and row._titleText
	if text and text ~= "" then
		return text
	end
	return nil
end

local function nativeMenuApiAvailable()
	return MenuUtil and type(MenuUtil.CreateContextMenu) == "function"
end

local function colorByte(value)
	value = math.max(0, math.min(1, tonumber(value) or 1))
	return math.floor((value * 255) + 0.5)
end

local function colorizeText(text, color)
	text = tostring(text or "")
	if type(color) ~= "table" then
		return text
	end
	return string.format(
		"|c%02x%02x%02x%02x%s|r",
		colorByte(color[4]),
		colorByte(color[1]),
		colorByte(color[2]),
		colorByte(color[3]),
		text)
end

function RCM:CreateTitleDivider(rootDescription)
	local created = false
	if rootDescription and type(rootDescription.CreateFrame) == "function" then
		local dividerDescription = rootDescription:CreateFrame()
		if dividerDescription and type(dividerDescription.AddInitializer) == "function" then
			dividerDescription:AddInitializer(function(frame)
				local divider = frame:AttachTexture()
				divider:SetPoint(
					"LEFT", frame, "LEFT", 0,
					ROW_CTX_TITLE_DIVIDER_VISUAL_OFFSET_Y)
				divider:SetPoint(
					"RIGHT", frame, "RIGHT", 0,
					ROW_CTX_TITLE_DIVIDER_VISUAL_OFFSET_Y)
				divider:SetAtlas(ROW_CTX_TITLE_DIVIDER_ATLAS, true)
				divider:SetVertexColor(
					ROW_CTX_TITLE_DIVIDER_COLOR[1],
					ROW_CTX_TITLE_DIVIDER_COLOR[2],
					ROW_CTX_TITLE_DIVIDER_COLOR[3])
				divider:SetAlpha(ROW_CTX_TITLE_DIVIDER_ALPHA)
				return 1, ROW_CTX_TITLE_DIVIDER_SLOT_HEIGHT
			end)
			created = true
		end
	end
	if not created
		and rootDescription
		and type(rootDescription.CreateDivider) == "function"
	then
		rootDescription:CreateDivider()
	end
	if rootDescription and type(rootDescription.CreateSpacer) == "function" then
		rootDescription:CreateSpacer(ROW_CTX_TITLE_DIVIDER_ACTION_GAP)
	end
end

local function createNativeMenuItem(rootDescription, item)
	if type(item) ~= "table" then
		return
	end
	local text = item.disabled and tostring(item.text or "")
		or colorizeText(item.text, item.textColor)
	if item.isTitle then
		rootDescription:CreateTitle(text)
		RCM:CreateTitleDivider(rootDescription)
		return
	end
	local description = rootDescription:CreateButton(text, function()
		if type(item.func) == "function" then
			item.func()
		end
	end)
	if description and type(description.SetEnabled) == "function" then
		description:SetEnabled(item.disabled ~= true)
	end
end

function RCM:ShowMenu(menuItems, options)
	options = type(options) == "table" and options or {}
	local onClose = type(options.onClose) == "function" and options.onClose or nil
	local released = false
	local function finishMenu()
		if released then
			return
		end
		released = true
		if onClose then
			onClose()
		end
	end
	if not nativeMenuApiAvailable() then
		finishMenu()
		return false
	end
	closeMenu()
	local releaseRegistered = false
	local motionRegistered = false
	local owner = options.owner or options.anchorFrame or UIParent
	local menu = MenuUtil.CreateContextMenu(owner, function(_, rootDescription)
		if type(rootDescription.SetTag) == "function" then
			rootDescription:SetTag("MENU_GROUPFINDER_CONTEXT")
		end
		if GF.UI and GF.UI.InstallMenuOpenAnimation then
			motionRegistered = GF.UI.InstallMenuOpenAnimation(
				rootDescription,
				{ preset = "menu", groupFinderOwned = true })
		end
		if onClose and type(rootDescription.AddMenuReleasedCallback) == "function" then
			releaseRegistered = true
			rootDescription:AddMenuReleasedCallback(finishMenu)
		end
		for itemIndex = 1, #(menuItems or {}) do
			createNativeMenuItem(rootDescription, menuItems[itemIndex])
		end
	end)
	if not menu then
		finishMenu()
		return false
	end
	if not motionRegistered and GF.UI and GF.UI.PlayPopupOpenAnimation then
		GF.UI.PlayPopupOpenAnimation(
			menu,
			{ preset = "menu", groupFinderOwned = true })
	end
	if onClose and not releaseRegistered then
		finishMenu()
	end
	return true
end

local function appendMenuItem(items, item)
	items[#items + 1] = item
end

local function whisperLeader(leaderName)
	if type(leaderName) ~= "string" or leaderName == "" then
		return false
	end
	local directTell = ChatFrameUtil and ChatFrameUtil.SendTell
	if type(directTell) == "function" then
		directTell(leaderName)
		return true
	end
	if type(ChatFrame_OpenChat) == "function" then
		local command = "/w " .. leaderName .. " "
		ChatFrame_OpenChat(command, SELECTED_DOCK_FRAME)
		return true
	end
	return false
end

local function canCopyCharacterName(name)
	if type(name) ~= "string" or name == "" then
		return false
	end
	if type(issecretvalue) == "function" and issecretvalue(name) then
		return false
	end
	return GF.UI and type(GF.UI.ShowCharacterNameCopyDialog) == "function"
end

local function blocklistIsEnabled()
	local blocklist = GF.Blocklist
	local query = blocklist and blocklist.IsEnabled
	return type(query) == "function" and query(blocklist) == true
end

local function getLiveCurrentGroupTarget(elementData)
	local projection = GF.CurrentGroupProjection
	if projection and type(projection.GetLiveActionTarget) == "function" then
		return projection:GetLiveActionTarget(elementData)
	end
	return nil
end

function RCM:BuildMenuItems(row, index, resultID, info)
	local L = GF.L or {}
	local displayTitle = getRowDisplayTitle(row)
	local censored = GF.Result and GF.Result.IsCensoredSearchResult
		and GF.Result:IsCensoredSearchResult(info)
	local titleText
	if censored then
		titleText = rawget(_G, "CENSORED_LFG_GROUP_NAME")
			or L.CENSORED_RESULT_HIDDEN_TITLE
			or "招募内容已隐藏"
	else
		titleText = displayTitle or (row and row._titleText) or info.name or "?"
	end
	local leaderName = info.leaderName
	local apply = GF.Apply
	local selectionAllowed = not (apply and apply.CanSelectRow)
		or apply:CanSelectRow(index, resultID) == true
	local copyAllowed = canCopyCharacterName(leaderName)
	local items = {}
	appendMenuItem(items, {
		isTitle = true,
		text = titleText,
		textColor = censored and ROW_CTX_CENSORED_TITLE_COLOR or nil,
	})
	appendMenuItem(items, {
		text = L.SIGN_UP or "Sign Up",
		textColor = ROW_CTX_SIGN_UP_COLOR,
		disabled = not selectionAllowed,
		func = function()
			local show = GF.Apply and GF.Apply.ShowDialogForIndex
			if show then
				show(GF.Apply, index, resultID)
			end
		end,
	})
	appendMenuItem(items, {
		text = L.CTX_WHISPER_LEADER or WHISPER_LEADER or "Whisper leader",
		disabled = not leaderName,
		func = function() whisperLeader(leaderName) end,
	})
	appendMenuItem(items, {
		text = L.CTX_COPY_LEADER_NAME or "复制队长名称",
		disabled = not copyAllowed,
		func = function()
			if copyAllowed then
				GF.UI.ShowCharacterNameCopyDialog(leaderName)
			end
		end,
	})
	if blocklistIsEnabled() then
		appendMenuItem(items, {
			text = L.CTX_BLOCK_TITLE or "Block same-title group",
			disabled = censored or not leaderName,
			func = function()
				GF.Blocklist:BlockSameTitleFromSearchResult(resultID, info, displayTitle)
			end,
		})
	end
	if type(LFGList_ReportListing) == "function" then
		appendMenuItem(items, {
			text = L.CTX_REPORT or LFG_LIST_REPORT_GROUP_FOR or "Report",
			func = function()
				LFGList_ReportListing(resultID, leaderName)
			end,
		})
	end
	if type(LFGList_ReportAdvertisement) == "function" then
		appendMenuItem(items, {
			text = L.CTX_REPORT_ADVERTISEMENT or "Report advertisement",
			textColor = ROW_CTX_REPORT_AD_COLOR,
			func = function()
				LFGList_ReportAdvertisement(resultID)
				local addedToBlocklist = false
				if blocklistIsEnabled() then
					addedToBlocklist = GF.Blocklist:BlockAdvertisementFromSearchResult(
						resultID, info) == true
				end
				if GF.Blocklist and type(GF.Blocklist.TipAdvertisementReport) == "function" then
					GF.Blocklist:TipAdvertisementReport(leaderName, addedToBlocklist)
				end
			end,
		})
	end
	if blocklistIsEnabled() then
		appendMenuItem(items, {
			text = L.CTX_BLOCK_LEADER or "Block leader",
			textColor = ROW_CTX_BLOCK_LEADER_COLOR,
			disabled = not leaderName,
			func = function()
				GF.Blocklist:BlockLeaderFromSearchResult(resultID, info)
			end,
		})
	end
	appendMenuItem(items, {
		text = L.CANCEL or "Cancel",
		func = closeMenu,
	})
	return items
end

function RCM:BuildCurrentGroupMenuItems(row, elementData)
	local entry = elementData and elementData.entry
	local info = entry and entry.info
	if type(info) ~= "table" then
		return nil
	end
	local L = GF.L or {}
	local displayTitle = getRowDisplayTitle(row)
	local censored = GF.Result and GF.Result.IsCensoredSearchResult
		and GF.Result:IsCensoredSearchResult(info)
	local titleText = censored
		and (rawget(_G, "CENSORED_LFG_GROUP_NAME")
			or L.CENSORED_RESULT_HIDDEN_TITLE
			or "招募内容已隐藏")
		or displayTitle or info.name or "?"
	local leaderName = info.leaderName
	local copyAllowed = canCopyCharacterName(leaderName)
	local _, liveResultID = getLiveCurrentGroupTarget(elementData)
	local canReport = liveResultID ~= nil
	local items = {}
	appendMenuItem(items, {
		isTitle = true,
		text = titleText,
		textColor = censored and ROW_CTX_CENSORED_TITLE_COLOR or nil,
	})
	appendMenuItem(items, {
		text = L.CTX_WHISPER_LEADER or WHISPER_LEADER or "Whisper leader",
		disabled = not leaderName,
		func = function() whisperLeader(leaderName) end,
	})
	appendMenuItem(items, {
		text = L.CTX_COPY_LEADER_NAME or "复制队长名称",
		disabled = not copyAllowed,
		func = function()
			if copyAllowed then
				GF.UI.ShowCharacterNameCopyDialog(leaderName)
			end
		end,
	})
	if blocklistIsEnabled() then
		appendMenuItem(items, {
			text = L.CTX_BLOCK_TITLE or "Block same-title group",
			disabled = censored or not leaderName,
			func = function()
				GF.Blocklist:BlockSameTitleFromSearchResult(
					nil, info, displayTitle)
			end,
		})
	end
	if type(LFGList_ReportListing) == "function" then
		appendMenuItem(items, {
			text = L.CTX_REPORT or LFG_LIST_REPORT_GROUP_FOR or "Report",
			disabled = not canReport,
			func = function()
				local _, resultID, liveInfo =
					getLiveCurrentGroupTarget(elementData)
				if resultID then
					LFGList_ReportListing(
						resultID,
						liveInfo and liveInfo.leaderName or leaderName)
				end
			end,
		})
	end
	if type(LFGList_ReportAdvertisement) == "function" then
		appendMenuItem(items, {
			text = L.CTX_REPORT_ADVERTISEMENT or "Report advertisement",
			textColor = ROW_CTX_REPORT_AD_COLOR,
			disabled = not canReport,
			func = function()
				local _, resultID, liveInfo =
					getLiveCurrentGroupTarget(elementData)
				if not resultID then
					return
				end
				LFGList_ReportAdvertisement(resultID)
				local addedToBlocklist = false
				if blocklistIsEnabled() then
					addedToBlocklist = GF.Blocklist:BlockAdvertisementFromSearchResult(
						resultID, liveInfo) == true
				end
				if GF.Blocklist
					and type(GF.Blocklist.TipAdvertisementReport) == "function"
				then
					GF.Blocklist:TipAdvertisementReport(
						liveInfo and liveInfo.leaderName or leaderName,
						addedToBlocklist)
				end
			end,
		})
	end
	if blocklistIsEnabled() then
		appendMenuItem(items, {
			text = L.CTX_BLOCK_LEADER or "Block leader",
			textColor = ROW_CTX_BLOCK_LEADER_COLOR,
			disabled = not leaderName,
			func = function()
				GF.Blocklist:BlockLeaderFromSearchResult(nil, info)
			end,
		})
	end
	appendMenuItem(items, {
		text = L.CANCEL or "Cancel",
		func = closeMenu,
	})
	return items
end

function RCM:ShowForRow(row)
	if not row then
		return
	end
	if row._gfCurrentGroupProjection == true then
		local items = self:BuildCurrentGroupMenuItems(
			row, row._gfCurrentGroupElement)
		if items then
			self:ShowMenu(items, { owner = row })
		end
		return
	end
	if not row.resultIndex then
		return
	end
	if GF.FindGroupTab and GF.FindGroupTab.SetSelectedRow then
		GF.FindGroupTab:SetSelectedRow(row)
	end
	local index = row.resultIndex
	local resultID = row.resultID or GF.Result:GetResultID(index)
	if not resultID then
		return
	end
	if GF.Apply and GF.Apply.ResolveApplyTarget then
		local resolvedIndex, resolvedID = GF.Apply:ResolveApplyTarget(index, resultID)
		if not resolvedIndex or not resolvedID then
			return
		end
		index, resultID = resolvedIndex, resolvedID
	end
	local info = GF.Result and GF.Result.GetAuthoritativeSearchResultInfo
		and GF.Result:GetAuthoritativeSearchResultInfo(resultID)
	if not info and C_LFGList and C_LFGList.GetSearchResultInfo then
		info = C_LFGList.GetSearchResultInfo(resultID)
	end
	if not info then
		return
	end
	self:ShowMenu(self:BuildMenuItems(row, index, resultID, info), {
		owner = row,
	})
end
