local _, GF = ...

GF.Search = {}

local function getLanguages()
	return C_LFGList.GetLanguageSearchFilter and C_LFGList.GetLanguageSearchFilter() or nil
end

local function hasKeywordSearch()
	local hasKeyword = false
	if GF.FindGroupTab and GF.FindGroupTab.GetBrowseSearchState then
		hasKeyword = GF.FindGroupTab:GetBrowseSearchState()
	end
	return hasKeyword == true
end

local function readCurrentResultIDs()
	local total, ids
	if hasKeywordSearch() and C_LFGList.GetFilteredSearchResults then
		total, ids = C_LFGList.GetFilteredSearchResults()
	elseif C_LFGList.GetSearchResults then
		total, ids = C_LFGList.GetSearchResults()
	end
	return total or 0, ids or {}
end

local function normalizeScope(selection, scope)
	if not scope then
		return nil
	end
	local categoryID = scope.categoryID or (selection and selection.categoryID)
	if not categoryID then
		return nil
	end
	local preferredFilters = scope.preferredFilters
		or (selection and selection.preferredFilters)
		or Enum.LFGListFilter.PvE
	local activityID = scope.activityID or (selection and selection.activityID)
	local activityIDsFilter = scope.activityIDsFilter
	if not activityIDsFilter and activityID and not scope.categoryBrowse
		and not (selection and selection.categoryBrowse) then
		activityIDsFilter = { activityID }
	end

	local filters = 0
	if not activityIDsFilter or #activityIDsFilter == 0 then
		local baseFilters = scope.filters or (selection and selection.filters) or 0
		filters = GF.Filter and GF.Filter:ResolveCategoryFilters(categoryID, baseFilters) or baseFilters
	end

	return {
		categoryID = categoryID,
		filters = filters,
		preferredFilters = preferredFilters,
		activityIDsFilter = activityIDsFilter,
	}
end

function GF.Search:Reset()
	self.pending = nil
	self.aggregateResultIDs = nil
	self.aggregateTotal = nil
	self.aggregateInfoByID = nil
end

function GF.Search:ClearAggregatedResultIDs()
	self.aggregateResultIDs = nil
	self.aggregateTotal = nil
	self.aggregateInfoByID = nil
end

function GF.Search:GetAggregatedResultIDs()
	return self.aggregateResultIDs, self.aggregateTotal, self.aggregateInfoByID
end

function GF.Search:BuildScopes(selection)
	local rawScopes
	if GF.NavData and GF.NavData.ResolveSearchScopes then
		rawScopes = GF.NavData.ResolveSearchScopes(selection, { forSearch = true })
	elseif selection and GF.NavData and GF.NavData.ResolveSearchScope then
		rawScopes = { GF.NavData.ResolveSearchScope(selection, { forSearch = true }) }
	elseif selection then
		rawScopes = { selection }
	else
		rawScopes = {}
	end

	local scopes = {}
	for _, scope in ipairs(rawScopes or {}) do
		local normalized = normalizeScope(selection, scope)
		if normalized then
			scopes[#scopes + 1] = normalized
		end
	end
	return scopes
end

function GF.Search:_RunScope(scope)
	if not scope or not scope.categoryID then
		return false
	end
	C_LFGList.Search(
		scope.categoryID,
		scope.filters or 0,
		scope.preferredFilters or Enum.LFGListFilter.PvE,
		getLanguages(),
		nil,
		nil,
		scope.activityIDsFilter
	)
	return true
end

function GF.Search:_RunPendingScope()
	local pending = self.pending
	if not pending then
		return false
	end
	return self:_RunScope(pending.scopes[pending.index])
end

function GF.Search:Run(selection)
	self:Reset()
	if GF.FindGroup and GF.FindGroup.IsSearchableSelection
		and not GF.FindGroup:IsSearchableSelection(selection) then
		return false
	end
	local scopes = self:BuildScopes(selection)
	if #scopes == 0 then
		return false
	end
	if #scopes == 1 then
		return self:_RunScope(scopes[1])
	end
	self.pending = {
		scopes = scopes,
		index = 1,
		resultIDs = {},
		seen = {},
		infoByID = {},
		total = 0,
	}
	return self:_RunPendingScope()
end

function GF.Search:OnSearchResults()
	local pending = self.pending
	if not pending then
		return "complete"
	end

	local total, ids = readCurrentResultIDs()
	pending.total = (pending.total or 0) + (total or 0)
	for _, resultID in ipairs(ids or {}) do
		if not pending.seen[resultID] then
			pending.seen[resultID] = true
			pending.resultIDs[#pending.resultIDs + 1] = resultID
		end
		if resultID and C_LFGList.GetSearchResultInfo and not pending.infoByID[resultID] then
			pending.infoByID[resultID] = C_LFGList.GetSearchResultInfo(resultID)
		end
	end

	if pending.index < #pending.scopes then
		pending.index = pending.index + 1
		if self:_RunPendingScope() then
			return "continue"
		end
		self.pending = nil
		self.aggregateResultIDs = nil
		self.aggregateTotal = nil
		self.aggregateInfoByID = nil
		return "failed"
	end

	self.aggregateResultIDs = pending.resultIDs
	self.aggregateTotal = #pending.resultIDs
	self.aggregateInfoByID = pending.infoByID
	self.pending = nil
	return "complete"
end

function GF.Search:OnSearchFailed()
	self.pending = nil
	self.aggregateResultIDs = nil
	self.aggregateTotal = nil
	self.aggregateInfoByID = nil
end
