if not WeakAurasSaved.Cyka then WeakAurasSaved.Cyka = {} end
if not WeakAurasSaved.Cyka.WhoSniffer then WeakAurasSaved.Cyka.WhoSniffer = {} end

aura_env.ignored = { "Maritza", "Goodbones" }

---@class aura_env
---@field raceMap table<string, string>
---@field stinkies table<string, Player>
---@field classColors table<string, string>
---@field whoQueries table<number, WHOQuery>
---@field ttl number
---@field messageQueue Message[]
---@field UpdateMacro fun()
---@field Notify fun(Player)
---@field NotifyGone fun(Player)
---@field queryPending boolean
---@field lastQuery WHOQuery

---@param input string
---@return number
local function utf8len(input)
	if not input then return 0 end
	local len = 0
	local i = 1
	local n = #input
	while i <= n do
		local c = input:byte(i)
		if c >= 0 and c <= 127 then
			i = i + 1
		elseif c >= 194 and c <= 223 then
			i = i + 2
		elseif c >= 224 and c <= 239 then
			i = i + 3
		elseif c >= 240 and c <= 244 then
			i = i + 4
		else
			i = i + 1
		end
		len = len + 1
	end
	return len
end
---@param input string
---@param targetLength number
---@param left boolean
---@return string
local function padString(input, targetLength, left)
	left = left or false
	local len = utf8len(input)
	if len < targetLength then
		if left then
			input = input .. string.rep(" ", targetLength - len)
		else
			input = string.rep(" ", targetLength - len) .. input
		end
	end
	return input
end

---@class WHOQuery
---@field query string
---@field filters WHOFilter[]
WHOQuery = {
	---@param query string
	---@param filters WHOFilter[]
	---@return WHOQuery
	new = function(query, filters)
		local self = setmetatable({}, {
			__index = WHOQuery,
		})
		self.query = query
		self.filters = filters
		return self
	end,
}

---@alias WHOFilter fun(name: string, guild: string, level: number, race: string, class: string, zone: string): boolean
---@type WHOFilter
local NotSiegeOfOrgrimmarFilter = function(name, guild, level, race, class, zone)
	if not zone then return false end
	return zone ~= "Siege of Orgrimmar"
end
---@type WHOFilter
local AllianceFilter = function(name, guild, level, race, class, zone)
	if not race then return false end
	if not aura_env.raceMap[race] then return false end
	return aura_env.raceMap[race] == "Alliance"
end

---@class Message
---@field message string
---@field channel string
---@field data string

if not WeakAurasSaved.Cyka.MessageQueue then WeakAurasSaved.Cyka.MessageQueue = {} end
aura_env.whoQueryIdx = 1
---@type table<number, WHOQuery>
aura_env.whoQueries = {}
aura_env.whoQueries[1] = WHOQuery.new('g-"БеспредеЛ"', {})
aura_env.whoQueries[2] = WHOQuery.new(
	'z-"Orgrimmar" z-"Durotar" z-"Valley of Trials" z-"Echo Isles" r-"Human" r-"Dwarf" r-"Night Elf"',
	{ NotSiegeOfOrgrimmarFilter, AllianceFilter }
)
aura_env.whoQueries[3] = WHOQuery.new(
	'z-"Orgrimmar" z-"Durotar" z-"Valley of Trials" z-"Echo Isles" r-"Gnome" r-"Draenei" r-"Worgen"',
	{ NotSiegeOfOrgrimmarFilter, AllianceFilter }
)
aura_env.whoQueries[4] = WHOQuery.new(
	'z-"Orgrimmar" z-"Durotar" z-"Valley of Trials" z-"Echo Isles" r-"Kul Tiran" r-"Dark Iron Dwarf" r-"Void Elf"',
	{ NotSiegeOfOrgrimmarFilter, AllianceFilter }
)
aura_env.whoQueries[5] = WHOQuery.new(
	'z-"Orgrimmar" z-"Durotar" z-"Valley of Trials" z-"Echo Isles" r-"Lightforged Draenei" r-"Mechagnome"',
	{ NotSiegeOfOrgrimmarFilter, AllianceFilter }
)
aura_env.whoQueries[6] = WHOQuery.new("Kekv Demonboo Dotmada Firobot Verminal", {})
aura_env.queryPending = false
aura_env.ttl = #aura_env.whoQueries * 2
aura_env.lastQuery = nil

---@type table<string, string>
aura_env.raceMap = {
	["Orc"] = "Horde",
	["Undead"] = "Horde",
	["Tauren"] = "Horde",
	["Troll"] = "Horde",
	["Blood Elf"] = "Horde",
	["Goblin"] = "Horde",
	["Human"] = "Alliance",
	["Dwarf"] = "Alliance",
	["Night Elf"] = "Alliance",
	["Gnome"] = "Alliance",
	["Draenei"] = "Alliance",
	["Worgen"] = "Alliance",
	["Vulpera"] = "Horde",
	["Nightborne"] = "Horde",
	["Zandalari Troll"] = "Horde",
	["Kul Tiran"] = "Alliance",
	["Dark Iron Dwarf"] = "Alliance",
	["Void Elf"] = "Alliance",
	["Lightforged Draenei"] = "Alliance",
	["Mechagnome"] = "Alliance",
	["Mag'har Orc"] = "Horde",
}

---@type table<string, Player>
aura_env.stinkies = {}

---@class Player
---@field name string
---@field guild string
---@field race string
---@field class string
---@field zone string
---@field lastSeenInternal number
---@field lastSeen string
---@field firstSeen string
---@field seenCount number
Player = {
	---@param name string
	---@param guild string
	---@param race string
	---@param class string
	---@param zone string
	---@return Player
	new = function(name, guild, race, class, zone)
		local self = setmetatable({}, {
			__index = Player,
		})
		self.name = name
		self.guild = guild
		self.race = race
		self.class = class
		self.zone = zone
		self.lastSeenInternal = GetTime()
		self.lastSeen = "never"
		self.firstSeen = "never"
		self.seenCount = 0
		return self
	end,
	ToString = function(self)
		local out = string.format(
			"%s %s %s\nFirst: %s Last: %s Seen: %3d",
			padString(self.name, 16, true),
			padString(self.guild, 26, false),
			padString(self.zone, 26, false),
			padString(self.firstSeen, 10, true),
			padString(self.lastSeen, 10, true),
			self.seenCount
		)
		return string.format("|cFF%s%s|r", aura_env.classColors[self.class], out)
	end,
}

---@type table<string, string>
aura_env.classColors = {
	["Warrior"] = "C69B6D",
	["Paladin"] = "F48CBA",
	["Hunter"] = "AAD372",
	["Rogue"] = "FFF468",
	["Priest"] = "FFFFFF",
	["Death Knight"] = "C41E3A",
	["Shaman"] = "0070DD",
	["Mage"] = "3FC7EB",
	["Warlock"] = "8788EE",
	["Monk"] = "00FF98",
	["Druid"] = "FF7C0A",
	["Demon Hunter"] = "A330C9",
}

---@param input string
---@param deliminer string
---@return string[], string|nil
local function StrSplit(input, deliminer)
	if not deliminer then return {}, "deliminer is nil" end
	if not input then return {}, "input is nil" end
	local parts = {}
	for part in string.gmatch(input, "([^" .. deliminer .. "]+)") do
		table.insert(parts, strtrim(part))
	end
	return parts, nil
end

---@type string[]
local toNotify = StrSplit(aura_env.config.notify, ",")
for i, part in ipairs(toNotify) do
	toNotify[i] = strtrim(part)
end
---@type table<string, boolean>
local notifyFor = {}
local notifyForD = StrSplit(aura_env.config.notifyFor, ",")
for i, part in ipairs(notifyForD) do
	notifyFor[part] = true
end

---@param player Player
---@return string
aura_env.MakeNotifyMessage = function(player)
	return string.format(
		"%s of class %s and guild %s in %s, first seen: %s, last seen: %s, times seen: %d",
		player.name,
		player.class,
		player.guild,
		player.zone,
		player.firstSeen,
		player.lastSeen,
		player.seenCount
	)
end
---@param msg string
local NotifyAll = function(msg)
	for _, rec in ipairs(toNotify) do
		---@type Message
		local message = {
			channel = "WHISPER",
			data = rec,
			message = msg,
		}
		table.insert(WeakAurasSaved.Cyka.MessageQueue, message)
	end
end
---@param msg string
local NotifyChannel = function(msg)
	local message = {
		channel = "CHANNEL",
		data = aura_env.config.channel,
		message = msg,
	}
	table.insert(WeakAurasSaved.Cyka.MessageQueue, message)
end

---@param player Player
---@return nil
aura_env.Notify = function(player)
	if not notifyFor[player.zone] then return end
	local msg = aura_env.MakeNotifyMessage(player)
	NotifyAll(msg)
	aura_env.NotifyChannel(player)
end

---@param player Player
---@param newzone string
---@return nil
aura_env.NotifyZoneChanged = function(player, newzone)
	if not notifyFor[newzone] then return end
	local msg =
		string.format("%s of class %s and guild %s moved to %s", player.name, player.class, player.guild, newzone)
	NotifyAll(msg)
	NotifyChannel(msg)
end

---@param player Player
---@return nil
aura_env.NotifyGone = function(player)
	if not notifyFor[player.zone] then return end
	local msg =
		string.format("%s of class %s and guild %s left %s", player.name, player.class, player.guild, player.zone)
	NotifyAll(msg)
	aura_env.NotifyChannelGone(player)
end

---@param player Player
---@return nil
aura_env.NotifyChannel = function(player)
	local msg = aura_env.MakeNotifyMessage(player)
	NotifyChannel(msg)
end

---@param player Player
---@return nil
aura_env.NotifyChannelGone = function(player)
	local msg =
		string.format("%s of class %s and guild %s left %s", player.name, player.class, player.guild, player.zone)
	NotifyChannel(msg)
end

aura_env.GetChannelName = function(channelId)
	local channels = { GetChannelList() }
	for i = 1, #channels, 2 do
		local id = channels[i]
		local name = channels[i + 1]
		if id == channelId then return name end
	end
	return nil
end
