local addonName, GF = ...

GF.addonName = addonName
GF.searching = false

local function call(owner, methodName, ...)
	local method = owner and owner[methodName]
	if method then
		return method(owner, ...)
	end
end

function GF.EnsureBlizzardAddons()
	if GF._blizzardAddonsReady then
		return true
	end

	local loader = C_AddOns and C_AddOns.LoadAddOn or LoadAddOn
	if loader then
		local dependencies = {
			"Blizzard_SharedXML",
			"Blizzard_Menu",
			"Blizzard_GroupFinder",
			"Blizzard_UIPanelTemplates",
			"Blizzard_Settings_Shared",
		}
		for index = 1, #dependencies do
			loader(dependencies[index])
		end
	end

	GF._blizzardAddonsReady = true
	return true
end

local dispatcher = CreateFrame("Frame")
GF.LFGEventDispatcher = dispatcher
local receivesIncrementalSearchUpdates = false

local incrementalSearchEvents = {
	"LFG_LIST_UPDATE_SEARCH_RESULTS",
}

function GF.SetLfgUpdateListening(shouldListen)
	shouldListen = shouldListen == true
	if receivesIncrementalSearchUpdates == shouldListen then
		return
	end

	receivesIncrementalSearchUpdates = shouldListen
	for index = 1, #incrementalSearchEvents do
		local eventName = incrementalSearchEvents[index]
		if shouldListen then
			dispatcher:RegisterEvent(eventName)
		else
			dispatcher:UnregisterEvent(eventName)
		end
	end
end

local function lfgDispatchIsSuspended()
	local availability = GF.Availability
	return availability and availability.ShouldProcessLfgEvent
		and availability:ShouldProcessLfgEvent() ~= true
end

local handlers = {}

function handlers.ADDON_LOADED(loadedName)
	if loadedName ~= addonName then
		if GF._addonLoaded and GF.Hook and GF.Hook.Refresh then
			GF.Hook.Refresh()
		end
		return
	end

	call(GF.Availability, "Init")
	GF.InitDB()
	call(GF.VersionDiscoveryService, "Init")
	call(GF.MythicPlusServices, "Init")
	call(GF.Blocklist, "Init")
	call(GF.BlacklistMenu, "Init")
	-- Install the 12.1 active-entry popup handoff before loading Blizzard's
	-- Group Finder through the prefer-open hook.
	call(GF.CensoredActiveEntryDialog, "Init")
	if GF.Hook and GF.Hook.Refresh then
		GF.Hook.Refresh()
	end
	call(GF.MinimapButton, "Init")
	call(GF.FloatButton, "Init")
	GF.NavData.RequestRefresh()
	GF._addonLoaded = true
end

function handlers.PLAYER_LOGIN()
	call(GF.BlacklistMenu, "Init")
	if GF.Hook and GF.Hook.Refresh then
		GF.Hook.Refresh()
	end
	if GF.ValidateLSMFontKeyAfterLogin then
		GF.ValidateLSMFontKeyAfterLogin()
	end
	call(GF.Blocklist, "ClearReadableTitleTokens")
	call(GF.Filter, "ApplyPersistedAdvancedFilter")
	call(GF.Apply, "OnGroupRosterChanged")
	call(GF.Apply, "InitInviteDialogHooks")
	GF.NavData.Rebuild()
	call(GF.NavTree, "Refresh")
	call(GF.FloatButton, "RefreshAlert")
	call(GF.JoinAnnounce, "Init")
	call(GF.CensoredActiveEntryDialog, "Refresh", "PLAYER_LOGIN", {
		showPopup = true,
	})
end

function handlers.LFG_LIST_SEARCH_RESULTS_RECEIVED()
	if not lfgDispatchIsSuspended() then
		call(GF.MainFrame, "OnSearchResults")
	end
end

function handlers.LFG_LIST_SEARCH_FAILED()
	if not lfgDispatchIsSuspended() then
		call(GF.MainFrame, "OnSearchFailed")
	end
end


function handlers.LFG_LIST_SEARCH_RESULT_UPDATED(resultID)
	if not lfgDispatchIsSuspended() then
		if receivesIncrementalSearchUpdates then
			call(GF.FindGroupTab, "OnSearchResultUpdated", resultID)
		end
	end
end

function handlers.LFG_LIST_UPDATE_SEARCH_RESULTS()
	if not lfgDispatchIsSuspended() then
		call(GF.FindGroupTab, "OnSearchResultsUpdated")
	end
end

function handlers.LFG_LIST_APPLICATION_STATUS_UPDATED(resultID, newStatus)
	if lfgDispatchIsSuspended() then
		return
	end
	call(GF.FindGroupTab, "OnApplicationStatusUpdated", resultID, newStatus)
	call(GF.JoinAnnounce, "OnApplicationStatusUpdated", resultID, newStatus)
	call(GF.FloatButton, "RefreshAlert")
end

function handlers.LFG_LIST_JOINED_GROUP(resultID)
	-- groupName 是受保护文本；当前队伍身份只消费 resultID/partyGUID。
	call(GF.Apply, "OnJoinedGroup", resultID)
end

function handlers.LFG_LIST_AVAILABILITY_UPDATE()
	-- Navigation and the seasonal Mythic+ scope share the availability cache.
	-- Clear it first so a scheduled season rebuild cannot consume login-time data.
	if GF.NavData and GF.NavData.ClearBuildCaches then
		GF.NavData.ClearBuildCaches()
	end
	call(GF.MythicPlusSeason, "RequestRefresh", "LFG_LIST_AVAILABILITY_UPDATE")

	local availability = GF.Availability
	if availability and availability.UpdateRestrictedState then
		if availability:UpdateRestrictedState() then
			-- EnterRestricted hides the main frame. Still mark its navigation
			-- dirty so leaving the restricted state cannot restore old branches.
			call(GF.MainFrame, "OnAvailabilityUpdate")
			return
		end
	end
	call(GF.MainFrame, "OnAvailabilityUpdate")
end

local function handlePremadePermissionUpdate()
	-- Blizzard re-evaluates the native Group Finder buttons on these events.
	-- Refresh the global premade gate immediately, then ask LFGList to publish
	-- its authoritative activity update before rebuilding seasonal branches.
	call(GF.NavData, "RequestRefresh")
	call(GF.MainFrame, "OnPremadePermissionUpdate")
end

handlers.PLAYER_LEVEL_CHANGED = handlePremadePermissionUpdate
handlers.TRIAL_STATUS_UPDATE = handlePremadePermissionUpdate

function handlers.LFG_LIST_ACTIVE_ENTRY_UPDATE(createdNew)
	-- The 12.1 event payload is nullable. Keep the original value for the relist
	-- transaction, while legacy projections continue to consume a strict bool.
	local createdEventValue = createdNew
	createdNew = createdEventValue == true
	local listing = GF.RecruitmentSession
	local hasActive = listing and listing.HasActive and listing:HasActive() == true

	call(listing, "SyncEntryOwnership", hasActive)
	local questCreateOutcome = call(
		GF.QuestRecruitmentBridge,
		"HandleActiveEntryChanged",
		hasActive,
		createdNew
	)
	local bumpOutcome = call(
		listing,
		"HandleRelistEntryChanged",
		hasActive,
		createdEventValue)
	call(GF.InvitationScheduler, "HandleActiveEntryChanged", hasActive, createdNew)
	call(GF.CensoredActiveEntryDialog, "Refresh", "LFG_LIST_ACTIVE_ENTRY_UPDATE")
	if lfgDispatchIsSuspended() then
		return
	end

	call(GF.ApplicantAlertService, "SyncBaseline", hasActive, createdNew)
	call(GF.MainFrame, "OnActiveEntryUpdate", {
		hasActive = hasActive,
		createdNew = createdNew,
		questCreateOutcome = questCreateOutcome,
		bumpOutcome = bumpOutcome,
		bumpResolved = true,
		fromActiveEntryEvent = true,
	})
end

function handlers.LFG_LIST_CENSORED_ACTIVE_ENTRY_UPDATE(isCensored)
	call(GF.CensoredActiveEntryDialog, "Refresh",
		"LFG_LIST_CENSORED_ACTIVE_ENTRY_UPDATE", {
			showPopup = isCensored == true,
		})
end

function handlers.LFG_LIST_REVEALED_CENSORED_ACTIVE_ENTRY()
	-- Revealing only changes presentation; the unresolved query remains the
	-- authority for whether updates and relists are blocked.
	call(GF.CensoredActiveEntryDialog, "Refresh",
		"LFG_LIST_REVEALED_CENSORED_ACTIVE_ENTRY")
end

function handlers.LFG_LIST_ENTRY_CREATION_FAILED()
	local bumpOutcome = call(GF.RecruitmentSession, "HandleCreationFailed")
	local questCreateOutcome = call(GF.QuestRecruitmentBridge, "HandleCreationFailed")
	if not lfgDispatchIsSuspended() then
		call(GF.MainFrame, "OnActiveEntryUpdate", {
			creationFailed = true,
			questCreateOutcome = questCreateOutcome,
			bumpOutcome = bumpOutcome,
			bumpResolved = true,
		})
	end
end

function handlers.LFG_LIST_APPLICANT_LIST_UPDATED()
	if lfgDispatchIsSuspended() then
		return
	end
	-- Blizzard announces newly added applicant rows through the list-wide
	-- event.  The scheduler's initial empty pass intentionally stops, so this
	-- edge must wake it when the first real applicant arrives.  It remains
	-- non-authoritative for releasing the per-applicant duplicate ledger.
	call(GF.InvitationScheduler, "Queue",
		nil, "LFG_LIST_APPLICANT_LIST_UPDATED")
	call(GF.MainFrame, "OnApplicantsUpdate",
		"LFG_LIST_APPLICANT_LIST_UPDATED")
	call(GF.MythicPlusCarpoolView, "OnApplicantRolesChanged", "LFG_LIST_APPLICANT_LIST_UPDATED")
end

function handlers.LFG_LIST_APPLICANT_UPDATED(applicantID)
	if lfgDispatchIsSuspended() then
		return
	end

	call(GF.InvitationScheduler, "Queue",
		applicantID, "LFG_LIST_APPLICANT_UPDATED")
	call(GF.ApplicantAlertService, "HandleApplicantChanged", applicantID)
	if GF.ApplicantsPanel and GF.ApplicantsPanel.OnApplicantUpdated then
		GF.ApplicantsPanel:OnApplicantUpdated(applicantID, true)
	else
		call(GF.MainFrame, "OnApplicantsUpdate")
	end
	call(GF.MythicPlusCarpoolView, "OnApplicantRolesChanged", "LFG_LIST_APPLICANT_UPDATED")
	call(GF.FloatButton, "RefreshAlert")
end

local function handleRosterChange(event)
	call(GF.CensoredActiveEntryDialog, "HandlePublishPermissionChanged")
	if lfgDispatchIsSuspended() then
		return
	end
	call(GF.Apply, "OnGroupRosterChanged")
	call(GF.MainFrame, "OnGroupRosterChanged", event)
	call(GF.JoinAnnounce, "OnGroupRosterChanged")
end

function handlers.PARTY_LEADER_CHANGED()
	handleRosterChange("PARTY_LEADER_CHANGED")
end

function handlers.GROUP_ROSTER_UPDATE()
	handleRosterChange("GROUP_ROSTER_UPDATE")
end

function handlers.GROUP_JOINED(category, partyGUID)
	call(GF.Apply, "OnGroupJoined", category, partyGUID)
end

function handlers.GROUP_LEFT(category, partyGUID)
	call(GF.ApplicantsPanel, "OnGroupLeft", category, partyGUID)
	call(GF.Apply, "OnGroupLeft", category, partyGUID)
end

local function refreshRoleSurfaces()
	call(GF.MainFrame, "RefreshRoleSelectionButtons")
	call(GF.ApplicantsPanel, "UpdateFromActiveRoleSummary")
end

handlers.LFG_ROLE_UPDATE = refreshRoleSurfaces
handlers.PLAYER_ROLES_ASSIGNED = refreshRoleSurfaces

local function handleRoleCheck()
	if not lfgDispatchIsSuspended() then
		call(GF.Apply, "QueueAutoConfirmLfgListRoleCheck")
	end
end

handlers.LFG_ROLE_CHECK_SHOW = handleRoleCheck
handlers.LFG_ROLE_CHECK_UPDATE = handleRoleCheck

function handlers.PLAYER_SPECIALIZATION_CHANGED(unit)
	if unit == "player" then
		refreshRoleSurfaces()
		call(GF.FindGroupTab, "OnPlayerSpecializationChanged")
	end
end

local alwaysRegisteredEvents = {
	"ADDON_LOADED",
	"PLAYER_LOGIN",
	"LFG_LIST_SEARCH_RESULTS_RECEIVED",
	"LFG_LIST_SEARCH_RESULT_UPDATED",
	"LFG_LIST_SEARCH_FAILED",
	"LFG_LIST_AVAILABILITY_UPDATE",
	"LFG_LIST_ACTIVE_ENTRY_UPDATE",
	"LFG_LIST_ENTRY_CREATION_FAILED",
	"LFG_LIST_APPLICANT_LIST_UPDATED",
	"LFG_LIST_APPLICANT_UPDATED",
	"LFG_LIST_APPLICATION_STATUS_UPDATED",
	"LFG_LIST_JOINED_GROUP",
	"PARTY_LEADER_CHANGED",
	"GROUP_ROSTER_UPDATE",
	"GROUP_JOINED",
	"GROUP_LEFT",
	"LFG_ROLE_UPDATE",
	"LFG_ROLE_CHECK_SHOW",
	"LFG_ROLE_CHECK_UPDATE",
	"PLAYER_ROLES_ASSIGNED",
	"PLAYER_SPECIALIZATION_CHANGED",
	"PLAYER_LEVEL_CHANGED",
	"TRIAL_STATUS_UPDATE",
}

for index = 1, #alwaysRegisteredEvents do
	dispatcher:RegisterEvent(alwaysRegisteredEvents[index])
end

local capabilityRegisteredEvents = {
	"LFG_LIST_CENSORED_ACTIVE_ENTRY_UPDATE",
	"LFG_LIST_REVEALED_CENSORED_ACTIVE_ENTRY",
}

local isEventValid = C_EventUtils and C_EventUtils.IsEventValid
if type(isEventValid) == "function" then
	for index = 1, #capabilityRegisteredEvents do
		local eventName = capabilityRegisteredEvents[index]
		local ok, valid = pcall(isEventValid, eventName)
		if ok and valid == true then
			dispatcher:RegisterEvent(eventName)
		end
	end
end

dispatcher:SetScript("OnEvent", function(_, eventName, ...)
	local handler = handlers[eventName]
	if handler then
		handler(...)
	end
end)

local function toggleMainWindow()
	call(GF.MainFrame, "Toggle")
end

SLASH_GROUPFINDER1 = "/gf"
SLASH_GROUPFINDER2 = "/groupfinder"
SLASH_GROUPFINDER3 = "/队伍查找器"
SlashCmdList.GROUPFINDER = function(message)
	if not call(GF.Debug, "HandleSlashCommand", message) then
		toggleMainWindow()
	end
end

function GROUPFINDER_TOGGLE()
	toggleMainWindow()
end

_G.GroupFinder = GF
