aura_env.debug = true

---@class BaseCooldown
---@field name string
---@field filename string
---@field id number
---@field isOnCooldown boolean
---@field announced boolean
---@field remaining number
aura_env.BaseCooldown = {
	---@param id number
	---@param name string
	---@param playbackSpeed number?
	---@return BaseCooldown
	new = function(id, name, playbackSpeed)
		local self = setmetatable({}, {
			__index = aura_env.BaseCooldown,
		})
		self.id = id
		self.name = name
		name = string.gsub(name, " ", "_")
		name = string.gsub(name, "'", "")
		name = string.lower(name)
		self.filename = name
		if playbackSpeed == nil then
			self.filename = string.format("1x\\%s", self.filename)
		else
			self.filename = string.format("%.1fx\\%s", playbackSpeed, self.filename)
		end
		if aura_env.debug then print(string.format("Created BaseCooldown with filename %s", self.filename)) end
		self.isOnCooldown = false
		self.announced = false
		self.remaining = 0
		return self
	end,

	---@param self BaseCooldown
	---@param cooldownFunc fun(spellId: number): number, number
	Update = function(self, cooldownFunc)
		local start, duration = cooldownFunc(self.id)
		local isOnCooldown = start > 0 and duration > 3
		local remaining = start + duration - GetTime()

		if not isOnCooldown and self.isOnCooldown then
			self:playComplete()
			self.announced = false
		end
		if not self.announced and isOnCooldown and remaining < 10 then
			self:playSoon()
			self.announced = true
		end

		self.isOnCooldown = isOnCooldown
		self.remaining = remaining
	end,

	---@param self BaseCooldown
	playComplete = function(self)
		local soundFile = string.format("Interface\\Sounds\\alerts\\%s.ogg", self.filename)
		if aura_env.debug then
			print(string.format("%s is off cooldown, playing sound file at %s", self.filename, soundFile))
		end
		WeakAuras.ScanEvents("PLAY_SOUND", soundFile)
	end,

	---@param self BaseCooldown
	playSoon = function(self)
		local soundFile = string.format("Interface\\Sounds\\alerts\\%s_soon.ogg", self.filename)
		if aura_env.debug then
			print(string.format("%s is almost off cooldown, playing sound file at %s", self.filename, soundFile))
		end
		WeakAuras.ScanEvents("PLAY_SOUND", soundFile)
	end,
}

---@class Spell:BaseCooldown
aura_env.Spell = {
	---@param id number
	---@param name string
	---@param playbackSpeed number?
	---@return Spell
	new = function(id, name, playbackSpeed)
		local obj = aura_env.BaseCooldown.new(id, name, playbackSpeed)
		---@cast obj Spell
		setmetatable(obj, {
			__index = aura_env.Spell,
		})
		return obj
	end,
	Update = function(self) aura_env.BaseCooldown.Update(self, GetSpellCooldown) end,
	playComplete = aura_env.BaseCooldown.playComplete,
	playSoon = aura_env.BaseCooldown.playSoon,
}
---@class Item:BaseCooldown
aura_env.Item = {
	---@param id number
	---@param name string
	---@param playbackSpeed number?
	---@return Item
	new = function(id, name, playbackSpeed)
		local obj = aura_env.BaseCooldown.new(id, name, playbackSpeed)
		---@cast obj Item
		setmetatable(obj, {
			__index = aura_env.Item,
		})
		return obj
	end,
	Update = function(self) aura_env.BaseCooldown.Update(self, GetItemCooldown) end,
	playComplete = aura_env.BaseCooldown.playComplete,
	playSoon = aura_env.BaseCooldown.playSoon,
}
---@class Buff:BaseCooldown
---@field isActive boolean
aura_env.Buff = {
	---@param id number
	---@param name string
	---@param playbackSpeed number?
	---@return Buff
	new = function(id, name, playbackSpeed)
		local obj = aura_env.BaseCooldown.new(id, name, playbackSpeed)
		---@cast obj Buff
		setmetatable(obj, {
			__index = aura_env.Buff,
		})
		obj.isActive = false
		return obj
	end,
	Update = function(self)
		local duration, expiry = select(6, UnitBuff("player", self.name))

		local isActive = true
		if duration == nil or expiry == nil then
			isActive = false
			expiry = 0
		end
		local remaining = expiry - GetTime()

		if not isActive and self.isActive then
			self:playComplete() -- More like "PlayExpired"
			self.announced = false
		end
		if not self.announced and isActive and remaining < 5 then
			self:playSoon() -- More like "PlayExpiring"
			self.announced = true
		end

		self.isActive = isActive
		self.remaining = remaining
	end,
	---@param self Buff
	playComplete = function(self)
		local soundFile = string.format("Interface\\Sounds\\alerts\\%s_expired.ogg", self.filename)
		if aura_env.debug then
			print(string.format("%s expired, playing sound file at %s", self.filename, soundFile))
		end
		WeakAuras.ScanEvents("PLAY_SOUND", soundFile)
	end,
	---@param self Buff
	playSoon = function(self)
		local soundFile = string.format("Interface\\Sounds\\alerts\\%s_expiring.ogg", self.filename)
		if aura_env.debug then
			print(string.format("%s expiring, playing sound file at %s", self.filename, soundFile))
		end
		WeakAuras.ScanEvents("PLAY_SOUND", soundFile)
	end,
}

---@type table<BaseCooldown>|nil
aura_env.ActiveSet = nil

---@type table<string, table<BaseCooldown>>
aura_env.Cooldowns = {
	["Warrior"] = {
		aura_env.Spell.new(107574, "Avatar", 1.6),
		aura_env.Spell.new(1719, "Battle Cry", 1.6),
		aura_env.Spell.new(205545, "Odyns Fury", 1.6),
		aura_env.Spell.new(26297, "Berserking", 1.6),
		aura_env.Spell.new(12292, "Bloodbath", 1.6),
	},
	["Warlock"] = {
		aura_env.Spell.new(108416, "Dark Pact", 1.6),
		aura_env.Spell.new(104773, "Unending Resolve", 1.6),
		aura_env.Spell.new(196098, "Soul Harvest", 1.6),
		aura_env.Item.new(5512, "Healthstone", 1.6),
		aura_env.Buff.new(0, "Deadwind Harvester", 1.6),
	},
}
