local _, ns = ...
local SlickUI = ns.SlickUI

local CleanseCapabilities = {}
SlickUI.CleanseCapabilities = CleanseCapabilities

local function Types(...)
	local result = {}
	for index = 1, select("#", ...) do result[select(index, ...)] = true end
	return result
end

local function Specs(...)
	local result = {}
	for index = 1, select("#", ...) do result[select(index, ...)] = true end
	return result
end

-- Only friendly debuff removals belong here. Entries with no specialization
-- restriction are still gated by the live spell book, including PvP talents.
local CLASS_ABILITIES = {
	DEMONHUNTER = {
		{ id = 205604, types = Types("Magic") }, -- Reverse Magic (PvP talent)
	},
	DRUID = {
		{ id = 88423, specs = Specs(105), types = Types("Magic", "Curse", "Poison") }, -- Nature's Cure
		{ id = 2782, types = Types("Curse", "Poison") }, -- Remove Corruption
	},
	EVOKER = {
		{ id = 360823, specs = Specs(1468), types = Types("Magic", "Poison") }, -- Naturalize
		{ id = 365585, specs = Specs(1467, 1473), types = Types("Poison") }, -- Expunge
		{ id = 374251, types = Types("Bleed", "Curse", "Disease", "Poison") }, -- Cauterizing Flame
	},
	HUNTER = {
		{ id = 212640, types = Types("Disease", "Poison") }, -- Mending Bandage (PvP talent)
	},
	MAGE = {
		{ id = 475, types = Types("Curse") }, -- Remove Curse
	},
	MONK = {
		{ id = 218164, specs = Specs(268, 269), types = Types("Disease", "Poison") }, -- Brewmaster/Windwalker Detox
		{ id = 115450, specs = Specs(270), types = Types("Magic") }, -- Mistweaver Detox
		{ id = 115450, specs = Specs(270), requires = 388874, types = Types("Disease", "Poison") }, -- Improved Detox
	},
	PALADIN = {
		{ id = 4987, specs = Specs(65), types = Types("Magic", "Disease", "Poison") }, -- Cleanse
		{ id = 213644, specs = Specs(66, 70), types = Types("Disease", "Poison") }, -- Cleanse Toxins
	},
	PRIEST = {
		{ id = 527, specs = Specs(256, 257), types = Types("Magic") }, -- Purify
		{ id = 527, specs = Specs(256, 257), requires = 390632, types = Types("Disease") }, -- Improved Purify
		{ id = 213634, specs = Specs(258), types = Types("Disease") }, -- Purify Disease
	},
	SHAMAN = {
		{ id = 77130, specs = Specs(264), types = Types("Magic", "Curse") }, -- Purify Spirit
		{ id = 51886, specs = Specs(264), types = Types("Magic", "Curse") }, -- Purify Spirit spell-book alias
		{ id = 51886, specs = Specs(262, 263), types = Types("Curse") }, -- Cleanse Spirit
	},
	WARLOCK = {
		{ id = 89808, bank = "pet", types = Types("Magic") }, -- Imp: Singe Magic
		{ id = 212623, bank = "pet", types = Types("Magic") }, -- Imp: Singe Magic (PvP)
		{ id = 115276, bank = "pet", types = Types("Magic") }, -- Fel Imp: Sear Magic
		{ id = 119898, matchesNameOf = { 89808, 115276 }, types = Types("Magic") }, -- Command Demon with a cleansing imp ability
	},
}

function CleanseCapabilities:CanRead(value)
	if canaccessvalue and not canaccessvalue(value) then return false end
	if issecretvalue and issecretvalue(value) then return false end
	return true
end

function CleanseCapabilities:GetSpecializationID()
	if type(GetSpecialization) ~= "function" or type(GetSpecializationInfo) ~= "function" then return nil end
	local specialization = GetSpecialization()
	if type(specialization) ~= "number" then return nil end
	local specializationID = GetSpecializationInfo(specialization)
	return type(specializationID) == "number" and specializationID or nil
end

function CleanseCapabilities:IsAbilityForCurrentSpec(ability)
	if type(ability.specs) ~= "table" then return true end
	local specializationID = self:GetSpecializationID()
	return specializationID ~= nil and ability.specs[specializationID] == true
end

function CleanseCapabilities:IsKnown(ability)
	if not ability or not self:IsAbilityForCurrentSpec(ability) then return false end
	if ability.requires and not self:IsKnown({ id = ability.requires }) then return false end
	if type(ability.matchesNameOf) == "table" then
		if not C_Spell or type(C_Spell.GetSpellName) ~= "function" then return false end
		local ok, currentName = pcall(C_Spell.GetSpellName, ability.id)
		if not ok or not self:CanRead(currentName) or type(currentName) ~= "string" then return false end
		local matched = false
		for _, comparisonID in ipairs(ability.matchesNameOf) do
			local nameOK, comparisonName = pcall(C_Spell.GetSpellName, comparisonID)
			if nameOK and self:CanRead(comparisonName) and currentName == comparisonName then
				matched = true
				break
			end
		end
		if not matched then return false end
	end
	local bankName = ability.bank == "pet" and "Pet" or "Player"
	local bank = Enum and Enum.SpellBookSpellBank and Enum.SpellBookSpellBank[bankName]
	if bank and C_SpellBook and type(C_SpellBook.IsSpellInSpellBook) == "function" then
		local ok, known = pcall(C_SpellBook.IsSpellInSpellBook, ability.id, bank, true)
		if ok and self:CanRead(known) and known == true then return true end
	end
	if ability.bank == "pet" then return false end
	if type(IsPlayerSpell) == "function" then
		local ok, known = pcall(IsPlayerSpell, ability.id)
		if ok and self:CanRead(known) and known == true then return true end
	end
	if type(IsSpellKnown) == "function" then
		local ok, known = pcall(IsSpellKnown, ability.id)
		if ok and self:CanRead(known) and known == true then return true end
	end
	if C_SpellBook and type(C_SpellBook.IsSpellKnown) == "function" then
		local ok, known = pcall(C_SpellBook.IsSpellKnown, ability.id)
		if ok and self:CanRead(known) and known == true then return true end
	end
	return false
end

function CleanseCapabilities:GetKnownAbilities()
	local _, classToken = UnitClass("player")
	local result = {}
	for _, ability in ipairs(CLASS_ABILITIES[classToken] or {}) do
		if self:IsKnown(ability) then result[#result + 1] = ability end
	end
	return result
end

function CleanseCapabilities:GetAbility(dispelType, readyPredicate)
	for _, ability in ipairs(self:GetKnownAbilities()) do
		if (dispelType == nil or ability.types[dispelType])
			and (readyPredicate == nil or readyPredicate(ability.id)) then
			return ability
		end
	end
	return nil
end

function CleanseCapabilities:CanCleanse(dispelType, readyPredicate)
	return type(dispelType) == "string" and self:GetAbility(dispelType, readyPredicate) ~= nil
end

function CleanseCapabilities:HasAny(readyPredicate)
	return self:GetAbility(nil, readyPredicate) ~= nil
end

function CleanseCapabilities:GetIcon(dispelType)
	local ability = self:GetAbility(dispelType) or self:GetAbility(nil)
	if not ability or not C_Spell or type(C_Spell.GetSpellTexture) ~= "function" then return nil end
	local ok, icon = pcall(C_Spell.GetSpellTexture, ability.id)
	return ok and self:CanRead(icon) and icon or nil
end
