local _, addon = ...
local L = addon.L;
local API = addon.API;
local JoinText = API.JoinText;


local ControlCenter = {};
addon.ControlCenter = ControlCenter;
ControlCenter.modules = {};
ControlCenter.newDBKeys = {};
ControlCenter.dbKeyXModule = {};
ControlCenter.changelogs = {};


local CategoryDefinition = {
	--[categoryIndex] = {key = string},
	--The key must match the keys in the localization

	[-2] = {key = "Beta"},

	[-1] = {key = "Timerunning"},

	[0] = {key = "Unknown"},    --Used during development


	[1] = {key = "General"},
	[2] = {key = "NPC Interaction"},
	[3] = {key = "Tooltip"},
	[4] = {key = "Class"},
	[5] = {key = "Reduction"},


	[1002] = {key = "Dragonflight", defaultCollapsed = true},

	[1208] = {key = "Plumber"},
};


local PrimaryCategory = {
	"Signature", "Current",
	"ActionBar", "Chat", "Collection", "Housing", "Instance", "Inventory", "Loot", "Map", "Profession", "Quest", "UnitFrame", "Old",
	"Uncategorized",
};


function ControlCenter:InitializeModules()
	--Initial Enable/Disable Modules

	local db = PlumberDB;
	local enabled, isForceEnabled;

	local timerunningSeason = API.GetTimerunningSeason();

	for _, moduleData in pairs(self.modules) do
		if moduleData.timerunningSeason and moduleData.timerunningSeason ~= timerunningSeason then
			moduleData.validityCheck = function()
				return false
			end
		end
	end

	local loadingErrors = {};

	for _, moduleData in pairs(self.modules) do
		isForceEnabled = false;
		if (not moduleData.validityCheck) or (moduleData.validityCheck()) then
			enabled = db[moduleData.dbKey] or moduleData.virtual;
			moduleData.isValid = true;

			if (not enabled) and (self.newDBKeys[moduleData.dbKey]) then
				enabled = true;
				isForceEnabled = true;
				db[moduleData.dbKey] = true;
			end

			if moduleData.requiredDBValues then
				for dbKey, value in pairs(moduleData.requiredDBValues) do
					if db[dbKey] ~= nil and db[dbKey] ~= value then
						enabled = false;
					end
				end
			end

			if moduleData.toggleFunc then
				if enabled then
					local success, result = pcall(moduleData.toggleFunc, enabled);
					if not success then
						table.insert(loadingErrors, result);
					end
				end
			else
				moduleData.virtual = true;
			end

			if moduleData.virtual then
				if moduleData.description then
					moduleData.description = moduleData.description.."\n\n"..L["Always On Module"];
				end
			end

			if enabled and isForceEnabled then
				API.PrintMessage(string.format(L["New Feature Auto Enabled Format"], moduleData.name));     --Todo: click link to view detail |cff71d5ff
			end

			self.dbKeyXModule[moduleData.dbKey] = moduleData;

			if moduleData.minimumTocVersion then
				if not addon.IsToCVersionEqualOrNewerThan(moduleData.minimumTocVersion) then
					moduleData.tocVersionCheckFailed = true;
				end
			end
		end
	end

	self.newDBKeys = {};


	--NewFeatureMark
	--/run PlumberDB.seenNewFeatureMark = nil
	if not db.seenNewFeatureMark then
		db.seenNewFeatureMark = {};
	end
	self.seenNewFeatureMark = db.seenNewFeatureMark;

	addon.CallbackRegistry:Trigger("ModulesLoaded");


	-- Display errors if any
	if loadingErrors[1] then
		for i, result in ipairs(loadingErrors) do
			C_Timer.After(0.1 * i, function()
				error(result);
			end);
		end
	end
end

function ControlCenter:AddModule(moduleData)
	--moduleData = {name = ModuleName, dbKey = PlumberDB[key], description = string, toggleFunc = function, validityCheck = function, categoryID = number, uiOrder = number}

	if not moduleData.categoryID then
		-- Assign a categoryID for modules added post settings revamp
		-- The old settings UI is only used in Classic
		moduleData.categoryID = 1;
		moduleData.uiOrder = 0;
	end

	table.insert(self.modules, moduleData);

	if moduleData.visibleInEditMode then
		addon.AddEditModeVisibleModule(moduleData);
	end
end

function ControlCenter:GetCategoryName(id)
	local key = CategoryDefinition[id] and CategoryDefinition[id].key;
	return key and L["Module Category "..key] or "Unknown Category"
end

function ControlCenter:GetValidModules()
	if self.validModules then
		return self.validModules
	end


	local settingsOpenTime = PlumberDB.settingsOpenTime;
	local canShowNewTag;
	if settingsOpenTime then
		canShowNewTag = true;
	else
		settingsOpenTime = 0;
		canShowNewTag = false;
	end
	settingsOpenTime = settingsOpenTime - 7 * 86400;    --NewFeatureMark gone after 7 days
	PlumberDB.settingsOpenTime = time()


	local function SortFunc_Module(a, b)
		if a.categoryID ~= b.categoryID then
			return a.categoryID < b.categoryID
		end

		if a.uiOrder ~= b.uiOrder then
			return a.uiOrder < b.uiOrder
			--should be finished here
		else
			if (a.categoryID == b.categoryID) and (a ~= b) then
				--print("Plumber: Duplicated Module uiOrder", a.uiOrder, a.name, b.name);   --debug
			end
		end

		if a.virtual ~= b.virtual then
			return not a.virtual
		end

		return a.name < b.name
	end

	table.sort(self.modules, SortFunc_Module);

	local validModules = {};
	local lastCategoryID;
	local numCate = 0;
	local numValid = 0;
	local subModules;

	for i, data in ipairs(self.modules) do
		if data.isValid then
			numValid = numValid + 1;
			if data.categoryID ~= lastCategoryID then
				lastCategoryID = data.categoryID;
				numCate = numCate + 1;
				validModules[numCate] = {
					categoryID = lastCategoryID,
					categoryName = self:GetCategoryName(lastCategoryID);
					subModules = {},
				};
				subModules = validModules[numCate].subModules;
			end

			if canShowNewTag and data.moduleAddedTime and data.moduleAddedTime > settingsOpenTime then
				data.isNewFeature = true;
			end

			table.insert(subModules, data);
		end
	end

	self.validModules = validModules;
	return validModules
end


function ControlCenter:GetModule(dbKey)
	return dbKey and self.dbKeyXModule[dbKey]
end

function ControlCenter:GetModuleDescription(dbKey)
	if dbKey and self.dbKeyXModule[dbKey] then
		return self.dbKeyXModule[dbKey].description
	end
end

function ControlCenter:GetModuleCategoryName(dbKey)
	local module = self:GetModule(dbKey);
	if module and module.categoryKeys then
		local text;
		for i, cateKey in ipairs(module.categoryKeys) do
			if i == 1 then
				text = self:GetPrimaryCategoryName(cateKey);
			else
				text = text..", "..self:GetPrimaryCategoryName(cateKey);
			end
		end
		return text
	end
end


do  --Our SuperTracking system is unused
	function ControlCenter:ShouldShowNavigatorOnDreamseedPins()
		return PlumberDB.Navigator_Dreamseed and not PlumberDB.Navigator_MasterSwitch
	end

	function ControlCenter:EnableSuperTracking()
		--PlumberDB.Navigator_MasterSwitch = true;
		--local SuperTrackFrame = addon.GetSuperTrackFrame();
		--SuperTrackFrame:TryEnableByModule();
	end
end


do  --Settings Panel Revamp
	local SortFunc = {};

	function SortFunc.Alphabet(a, b)
		if a.virtual ~= b.virtual then
			return not a.virtual
		end

		return a.name < b.name
	end

	function SortFunc.Date(a, b)
		if a.virtual ~= b.virtual then
			return not a.virtual
		end

		if a.moduleAddedTime and b.moduleAddedTime then
			return a.moduleAddedTime > b.moduleAddedTime
		end

		if not(a.moduleAddedTime or b.moduleAddedTime) then
			return a.name < b.name
		end

		return a.moduleAddedTime ~= nil
	end


	local CurrentSortMethod = SortFunc.Alphabet;

	local FilterSortByMethods = {
		SortFunc.Alphabet,
		SortFunc.Date,
	};

	function ControlCenter:ClearFilterCache()
		self.sortedModules = nil;
	end

	function ControlCenter:UpdateCurrentSortMethod()
		local index = PlumberDB and PlumberDB.SettingsPanelFilterIndex;
		index = self:GetValidSortMethodIndex(index);

		if CurrentSortMethod ~= FilterSortByMethods[index] then
			CurrentSortMethod = FilterSortByMethods[index];
			self:ClearFilterCache();
		end

		return index
	end

	function ControlCenter:SetCurrentSortMethod(index)
		if not (index and FilterSortByMethods[index]) then
			index = 1;
		end

		if CurrentSortMethod ~= FilterSortByMethods[index] then
			CurrentSortMethod = FilterSortByMethods[index];
			self.sortedModules = nil;
		end

		if PlumberDB then
			PlumberDB.SettingsPanelFilterIndex = index;
		end
	end

	function ControlCenter:GetValidSortMethodIndex(index)
		if not (index and FilterSortByMethods[index]) then
			index = 1;
		end
		return index
	end

	function ControlCenter:GetNumFilters()
		return #FilterSortByMethods
	end


	function ControlCenter:GetPrimaryCategoryName(categoryKey)
		return L["SC "..categoryKey] or "Unknown"
	end

	function ControlCenter:GetSearchTagName(tag)
		return L["KW "..tag]
	end

	function ControlCenter:UpdateSettingsOpenTime()
		local settingsOpenTime = PlumberDB.settingsOpenTime;
		local canShowNewTag;
		if settingsOpenTime then
			canShowNewTag = true;
		else
			settingsOpenTime = 0;
			canShowNewTag = false;
		end
		settingsOpenTime = settingsOpenTime - 7 * 86400;    --NewFeatureMark gone after 7 days
		PlumberDB.settingsOpenTime = time()
		return settingsOpenTime, canShowNewTag
	end

	function ControlCenter:IsNewFeatureMarkerSeen(dbKey)
		return self.seenNewFeatureMark[dbKey]
	end

	function ControlCenter:FlagNewFeatureMarkerSeen(dbKey)
		self.seenNewFeatureMark[dbKey] = true;
	end

	function ControlCenter:GetSortedModules()
		if ControlCenter.sortedModules then
			return ControlCenter.sortedModules
		end

		local settingsOpenTime, canShowNewTag = ControlCenter:UpdateSettingsOpenTime();
		local ipairs = ipairs;
		local tinsert = table.insert;
		local tbl = {};
		local numValid = 0;

		local categoryXModule = {};
		local anyNewFeatureInCategory = {};

		for i, data in ipairs(ControlCenter.modules) do
			if data.isValid then
				numValid = numValid + 1;

				if canShowNewTag and data.moduleAddedTime and data.moduleAddedTime > settingsOpenTime then
					if not ControlCenter:IsNewFeatureMarkerSeen(data.dbKey) then
						data.isNewFeature = true;
					end
				end

				if not data.categoryKeys then
					data.categoryKeys = {"Uncategorized"};
				end

				for _, cateKey in ipairs(data.categoryKeys) do
					if not categoryXModule[cateKey] then
						categoryXModule[cateKey] = {};
					end

					tinsert(categoryXModule[cateKey], data);

					if data.isNewFeature then
						anyNewFeatureInCategory[cateKey] = true;
					end
				end
			end
		end

		for cateKey, v in pairs(categoryXModule) do
			table.sort(v, CurrentSortMethod);
		end

		local numModules;

		for _, cateKey in ipairs(PrimaryCategory) do
			numModules = categoryXModule[cateKey] and #categoryXModule[cateKey] or 0;
			if numModules > 0 then
				tinsert(tbl, {
					key = cateKey,
					categoryName = ControlCenter:GetPrimaryCategoryName(cateKey),
					modules = categoryXModule[cateKey],
					anyNewFeature = anyNewFeatureInCategory[cateKey],
					numModules = numModules,
				});
			end
		end

		ControlCenter.sortedModules = tbl;
		return tbl
	end

	function ControlCenter:GetSearchResult(keyword)
		if not keyword then
			return self:GetSortedModules();
		end

		local settingsOpenTime, canShowNewTag = self:UpdateSettingsOpenTime();
		local lower = string.lower;
		local find = string.find;
		local ipairs = ipairs;
		local tinsert = table.insert;
		local tbl = {};
		local numValid = 0;

		local categoryXModule = {};
		local anyNewFeatureInCategory = {};
		local shownCategory = {};

		keyword = lower(keyword);

		for _, cateKey in ipairs(PrimaryCategory) do
			local categoryName = ControlCenter:GetPrimaryCategoryName(cateKey);
			if categoryName and find(lower(categoryName), keyword, 1, true) then
				shownCategory[cateKey] = true;
			end
		end

		local tagName;

		for i, data in ipairs(ControlCenter.modules) do
			if data.isValid then
				if not data.combinedSearchText then
					local text = lower(data.name);

					if data.searchTags then
						for _, tag in ipairs(data.searchTags) do
							tagName = self:GetSearchTagName(tag);
							text = JoinText(" ", text, lower(tagName));
						end
					end

					--Support searching in descriptions?
					if data.description then
						text = JoinText(" ", text, lower(data.description));
					end

					data.combinedSearchText = text;
				end

				local cateKey = data.categoryKeys and data.categoryKeys[1] or "Uncategorized";
				local matched = shownCategory[cateKey];

				if (not matched) and data.combinedSearchText then
					if find(data.combinedSearchText, keyword, 1, true) then
						matched = true;
					end
				end

				if matched then
					numValid = numValid + 1;

					if canShowNewTag and data.moduleAddedTime and data.moduleAddedTime > settingsOpenTime then
						if not ControlCenter:IsNewFeatureMarkerSeen(data.dbKey) then
							data.isNewFeature = true;
						end
					end

					if not categoryXModule[cateKey] then
						categoryXModule[cateKey] = {};
					end

					tinsert(categoryXModule[cateKey], data);

					if data.isNewFeature then
						anyNewFeatureInCategory[cateKey] = true;
					end
				end
			end
		end

		for cateKey, v in pairs(categoryXModule) do
			table.sort(v, CurrentSortMethod);
		end

		local numModules;

		for _, cateKey in ipairs(PrimaryCategory) do
			numModules = categoryXModule[cateKey] and #categoryXModule[cateKey] or 0;
			if numModules > 0 then
				tinsert(tbl, {
					key = cateKey,
					categoryName = ControlCenter:GetPrimaryCategoryName(cateKey),
					modules = categoryXModule[cateKey],
					anyNewFeature = anyNewFeatureInCategory[cateKey],
					numModules = numModules,
				});
			end
		end

		return tbl
	end

	function ControlCenter:FlagCurrentNewFeatureMarkerSeen()
		local settingsOpenTime, canShowNewTag = self:UpdateSettingsOpenTime();
		for i, data in ipairs(self.modules) do
			if data.isValid then
				data.isNewFeature = nil;
				if canShowNewTag and data.moduleAddedTime and data.moduleAddedTime > settingsOpenTime then
					self:FlagNewFeatureMarkerSeen(data.dbKey);
				end
			end
		end
		self:ClearFilterCache();
	end

	function ControlCenter:AnyNewFeatureMarker()
		local settingsOpenTime, canShowNewTag = self:UpdateSettingsOpenTime();
		for i, data in ipairs(self.modules) do
			if data.isValid then
				if canShowNewTag and data.moduleAddedTime and data.moduleAddedTime > settingsOpenTime and (not self:IsNewFeatureMarkerSeen(data.dbKey)) then
					return true
				end
			end
		end
	end
end


do  --Option: Always Enable New Features
	addon.CallbackRegistry:Register("NewDBKeysAdded", function(newDBKeys)
		ControlCenter.newDBKeys = newDBKeys;
	end);

	local function ToggleFunc_EnableNewByDefault(state)

	end

	local moduleData = {
		name = L["ModuleName EnableNewByDefault"],
		dbKey = "EnableNewByDefault",
		description = L["ModuleDescription EnableNewByDefault"],
		toggleFunc = ToggleFunc_EnableNewByDefault,
		categoryID = 1208,
		uiOrder = 1,
	};

	--ControlCenter:AddModule(moduleData);
end
