local _, GF = ...

GF.JoinAnnounce = {}
local JA = GF.JoinAnnounce

local ANNOUNCE_RETRY_MAX = 6
local ANNOUNCE_RETRY_DELAY = 0.5
local ANNOUNCED_TTL = 30

local function chat(message)
	if not message or message == "" then
		return
	end
	if GF.ShowStatusMessage then
		GF.ShowStatusMessage(message)
	elseif DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then
		DEFAULT_CHAT_FRAME:AddMessage(tostring(message))
	end
end

local function isReadableText(text)
	return type(text) == "string" and text ~= "" and text ~= "?"
end

local function getSearchResultInfo(resultID)
	if not resultID or not C_LFGList or not C_LFGList.GetSearchResultInfo then
		return nil
	end
	local ok, info = pcall(C_LFGList.GetSearchResultInfo, resultID)
	if ok then
		return info
	end
	return nil
end

local function getActivityTitle(info)
	local activityID = info and info.activityIDs and info.activityIDs[1] or info and info.activityID
	if not activityID or not C_LFGList or not C_LFGList.GetActivityInfoTable then
		return nil
	end
	local ok, activity = pcall(C_LFGList.GetActivityInfoTable, activityID, info.questID, info.isWarMode)
	if not ok or not activity then
		return nil
	end
	return activity.fullName or activity.shortName or activity.name
end

function JA:IsEnabled()
	local db = GF.GetDB and GF.GetDB()
	return db and db.joinAnnounceEnabled == true
end

function JA:Init()
	if self._inited then
		return
	end
	self._inited = true
	self._announced = {}
end

function JA:BuildGroupTitle(resultID)
	local info = getSearchResultInfo(resultID)
	if not info then
		return nil
	end
	local title
	if GF.Result and GF.Result.GetListingTitle then
		title = GF.Result:GetListingTitle(info, resultID)
	end
	if not isReadableText(title) then
		title = info.name
	end
	if isReadableText(title) then
		return title
	end
	local activityTitle = getActivityTitle(info)
	if isReadableText(activityTitle) then
		return activityTitle
	end
	return nil
end

function JA:MarkAnnounced(resultID)
	self._announced = self._announced or {}
	local now = GetTime and GetTime() or 0
	self._announced[resultID] = now + ANNOUNCED_TTL
	for id, expires in pairs(self._announced) do
		if expires <= now then
			self._announced[id] = nil
		end
	end
end

function JA:WasRecentlyAnnounced(resultID)
	local expires = self._announced and self._announced[resultID]
	return expires and expires > ((GetTime and GetTime()) or 0)
end

function JA:Announce(resultID, allowFallback)
	if not resultID or self:WasRecentlyAnnounced(resultID) then
		return true
	end
	local L = GF.L or {}
	local title = self:BuildGroupTitle(resultID)
	if isReadableText(title) then
		chat(string.format(L.JOIN_ANNOUNCE_JOINED_TITLE_FMT or "GroupFinder: joined group: %s", title))
		self:MarkAnnounced(resultID)
		return true
	end
	if allowFallback then
		chat(L.JOIN_ANNOUNCE_JOINED or "GroupFinder: joined group.")
		self:MarkAnnounced(resultID)
		return true
	end
	return false
end

function JA:ScheduleRetry()
	local pending = self._pending
	if not pending or pending.timer then
		return
	end
	if not C_Timer or not C_Timer.After then
		self:TryAnnounce(pending.token, true)
		return
	end
	pending.timer = true
	C_Timer.After(ANNOUNCE_RETRY_DELAY, function()
		if not JA._pending or JA._pending.token ~= pending.token then
			return
		end
		JA._pending.timer = nil
		JA:TryAnnounce(pending.token)
	end)
end

function JA:TryAnnounce(token, force)
	local pending = self._pending
	if not pending or pending.token ~= token then
		return
	end
	if self:Announce(pending.resultID, force) then
		self._pending = nil
		return
	end
	pending.attempts = (pending.attempts or 0) + 1
	if pending.attempts >= ANNOUNCE_RETRY_MAX then
		self:Announce(pending.resultID, true)
		self._pending = nil
		return
	end
	self:ScheduleRetry()
end

function JA:Queue(resultID)
	if not self:IsEnabled() or not resultID or self:WasRecentlyAnnounced(resultID) then
		return
	end
	self._token = (self._token or 0) + 1
	self._pending = {
		resultID = resultID,
		token = self._token,
		attempts = 0,
	}
	self:TryAnnounce(self._token)
end

function JA:OnApplicationStatusUpdated(resultID, newStatus)
	if newStatus ~= "inviteaccepted" then
		return
	end
	self:Queue(resultID)
end

function JA:OnGroupRosterChanged()
	local pending = self._pending
	if pending then
		self:TryAnnounce(pending.token)
	end
end
