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

local MythicKeys = {
	Prefix = "SlickUIKeys",
	AstralPrefix = "AstralKeys",
	Protocol = "V1",
	VisibleRows = 15,
	ScrollOffset = 0,
	Filtered = {},
	LastOwnSignature = nil,
	LastAstralSignature = nil,
}
SlickUI.MythicKeys = MythicKeys

local function IsReadable(value)
	if canaccessvalue and not canaccessvalue(value) then return false end
	if issecretvalue and issecretvalue(value) then return false end
	return value ~= nil
end

local function SafeString(value)
	return IsReadable(value) and type(value) == "string" and value or nil
end

local function SafeNumber(value)
	return IsReadable(value) and type(value) == "number" and value or nil
end

local function ShortName(name)
	name = SafeString(name)
	return name and Ambiguate and Ambiguate(name, "short") or name or "Unknown"
end

local function FullPlayerName()
	local name, realm = UnitFullName("player")
	name = SafeString(name) or SafeString(UnitName("player")) or "Unknown"
	realm = SafeString(realm)
	if realm and realm ~= "" then return name .. "-" .. realm:gsub("%s+", "") end
	return name
end

local function CacheKey(name)
	return string.lower((SafeString(name) or "unknown"):gsub("%s+", ""))
end

local function ClassColor(classFile)
	local color = classFile and RAID_CLASS_COLORS and RAID_CLASS_COLORS[classFile]
	return color and color.r or 0.92, color and color.g or 0.94, color and color.b or 1.00
end

local function DungeonName(mapID)
	mapID = tonumber(mapID)
	if not mapID or mapID <= 0 or not C_ChallengeMode or type(C_ChallengeMode.GetMapUIInfo) ~= "function" then return "No Keystone" end
	local ok, name = pcall(C_ChallengeMode.GetMapUIInfo, mapID)
	return ok and SafeString(name) and name ~= "" and name or ("Dungeon " .. mapID)
end

local function RelativeAge(timestamp)
	timestamp = tonumber(timestamp)
	if not timestamp or timestamp <= 0 then return "Unknown" end
	local age = math.max(0, (time and time() or 0) - timestamp)
	if age < 60 then return "Now" end
	if age < 3600 then return math.floor(age / 60) .. "m" end
	if age < 86400 then return math.floor(age / 3600) .. "h" end
	return math.floor(age / 86400) .. "d"
end

local function SourceLabel(entry)
	if entry.source == "self" then return "This character" end
	if entry.source == "astral" then
		return entry.scope and ("Astral: " .. entry.scope) or "Astral Keys"
	end
	if entry.scope == "PARTY" or entry.scope == "RAID" then return "Party / Raid" end
	if entry.scope == "GUILD" then return "Guild" end
	if entry.scope == "WHISPER" or entry.scope == "BNET" then return "Friends" end
	return "SlickUI"
end

local function CurrentWeeklyBest()
	local best = 0
	if C_MythicPlus and type(C_MythicPlus.GetRunHistory) == "function" then
		local ok, history = pcall(C_MythicPlus.GetRunHistory, false, true)
		if ok and type(history) == "table" then
			for _, run in ipairs(history) do
				if type(run) == "table" and run.thisWeek and SafeNumber(run.level) then best = math.max(best, run.level) end
			end
		end
	end
	return best
end

local function CurrentScore()
	if C_PlayerInfo and type(C_PlayerInfo.GetPlayerMythicPlusRatingSummary) == "function" then
		local ok, summary = pcall(C_PlayerInfo.GetPlayerMythicPlusRatingSummary, "player")
		if ok and type(summary) == "table" then return SafeNumber(summary.currentSeasonScore) or 0 end
	end
	return 0
end

local function CurrentFaction()
	local faction = UnitFactionGroup and UnitFactionGroup("player")
	return SafeString(faction) or "Neutral"
end

function MythicKeys:GetDB()
	return SlickUI.DB.mythicKeys
end

function MythicKeys:MergeEntry(entry, force, silent)
	if type(entry) ~= "table" or not SafeString(entry.name) then return false end
	entry.mapID = tonumber(entry.mapID) or 0
	entry.level = tonumber(entry.level) or 0
	entry.weeklyBest = tonumber(entry.weeklyBest) or 0
	entry.score = tonumber(entry.score) or 0
	entry.timestamp = tonumber(entry.timestamp) or (time and time() or 0)
	entry.classFile = SafeString(entry.classFile) or "PRIEST"
	entry.source = SafeString(entry.source) or "slickui"
	entry.scope = SafeString(entry.scope) or "UNKNOWN"
	entry.faction = SafeString(entry.faction) or "Neutral"
	if #entry.name > 80 or #entry.classFile > 20 or entry.mapID < 0 or entry.level < 0 or entry.level > 99 then return false end
	local now = time and time() or 0
	if entry.timestamp > now + 300 then entry.timestamp = now end
	local key = CacheKey(entry.name)
	local current = self:GetDB().cache[key]
	local currentStamp = current and tonumber(current.timestamp) or 0
	if current and current.source == "self" and entry.source ~= "self" then return false end
	if not force and current and currentStamp > entry.timestamp and current.source ~= "astral" then return false end
	self:GetDB().cache[key] = entry
	if not silent then self:RefreshDisplays() end
	return true
end

function MythicKeys:GetOwnEntry()
	local mapID, level
	if C_MythicPlus then
		if type(C_MythicPlus.GetOwnedKeystoneChallengeMapID) == "function" then
			local ok, value = pcall(C_MythicPlus.GetOwnedKeystoneChallengeMapID)
			if ok then mapID = SafeNumber(value) end
		end
		if type(C_MythicPlus.GetOwnedKeystoneLevel) == "function" then
			local ok, value = pcall(C_MythicPlus.GetOwnedKeystoneLevel)
			if ok then level = SafeNumber(value) end
		end
	end
	local _, classFile = UnitClass("player")
	return {
		name = FullPlayerName(), classFile = SafeString(classFile) or "PRIEST",
		mapID = mapID or 0, level = level or 0, weeklyBest = CurrentWeeklyBest(), score = CurrentScore(),
		faction = CurrentFaction(), timestamp = time and time() or 0, source = "self", scope = "SELF",
	}
end

function MythicKeys:Serialize(entry)
	return table.concat({ self.Protocol, entry.name, entry.classFile, entry.mapID, entry.level, entry.weeklyBest, entry.score, entry.faction, entry.timestamp }, "|")
end

function MythicKeys:ParseNative(message, channel)
	if not SafeString(message) then return nil end
	local version, name, classFile, mapID, level, weeklyBest, score, faction, timestamp = strsplit("|", message)
	if version ~= self.Protocol or not name or not classFile then return nil end
	return {
		name = name, classFile = classFile, mapID = tonumber(mapID), level = tonumber(level),
		weeklyBest = tonumber(weeklyBest), score = tonumber(score), faction = faction,
		timestamp = tonumber(timestamp), source = "slickui", scope = channel,
	}
end

function MythicKeys:BroadcastNative(entry)
	if not C_ChatInfo or type(C_ChatInfo.SendAddonMessage) ~= "function" then return end
	local message = self:Serialize(entry)
	if IsInGuild and IsInGuild() then pcall(C_ChatInfo.SendAddonMessage, self.Prefix, message, "GUILD") end
	if IsInRaid and IsInRaid() then
		pcall(C_ChatInfo.SendAddonMessage, self.Prefix, message, "RAID")
	elseif IsInGroup and IsInGroup() then
		pcall(C_ChatInfo.SendAddonMessage, self.Prefix, message, "PARTY")
	end
end

function MythicKeys:AstralWeekForPlayer()
	if type(AstralKeys) == "table" then
		local player = CacheKey(FullPlayerName())
		for _, entry in ipairs(AstralKeys) do
			if type(entry) == "table" and CacheKey(entry.unit) == player then return tonumber(entry.week) or 0 end
		end
	end
	return 0
end

function MythicKeys:BroadcastAstral(entry)
	if not IsInGuild or not IsInGuild() or not C_ChatInfo or type(C_ChatInfo.SendAddonMessage) ~= "function" then return end
	local payload = string.format("updateV9 %s:%s:%d:%d:%d:%d:%d", entry.name, entry.classFile, entry.mapID, entry.level, entry.weeklyBest, self:AstralWeekForPlayer(), entry.score)
	pcall(C_ChatInfo.SendAddonMessage, self.AstralPrefix, payload, "GUILD")
end

function MythicKeys:RefreshOwnKey(force)
	local entry = self:GetOwnEntry()
	local signature = table.concat({ entry.mapID, entry.level, entry.weeklyBest, entry.score }, ":")
	entry.timestamp = time and time() or 0
	self:MergeEntry(entry, true)
	if force or signature ~= self.LastOwnSignature then
		self.LastOwnSignature = signature
		self:BroadcastNative(entry)
		self:BroadcastAstral(entry)
	end
end

function MythicKeys:ImportAstralCache()
	if type(AstralKeys) ~= "table" then return end
	local signatures = {}
	for _, value in ipairs(AstralKeys) do
		if type(value) == "table" and SafeString(value.unit) then
			local signature = table.concat({ value.unit, value.dungeon_id or 0, value.key_level or 0, value.weekly_best or 0, value.mplus_score or 0, value.time_stamp or 0 }, ":")
			signatures[#signatures + 1] = signature
		end
	end
	table.sort(signatures)
	local combined = table.concat(signatures, "|")
	if combined == self.LastAstralSignature then return end
	self.LastAstralSignature = combined
	for _, value in ipairs(AstralKeys) do
		if type(value) == "table" and SafeString(value.unit) then
			self:MergeEntry({
				name = value.unit, classFile = value.class, mapID = value.dungeon_id, level = value.key_level,
				weeklyBest = value.weekly_best, score = value.mplus_score, faction = value.faction,
				timestamp = time and time() or 0, source = "astral", scope = value.source or "cache",
			}, false, true)
		end
	end
	self:RefreshDisplays()
end

function MythicKeys:ParseAstralRecord(record, kind, channel)
	local values = { strsplit(":", record) }
	local name, classFile = values[1], values[2]
	if not SafeString(name) or not SafeString(classFile) then return nil end
	local mapID, level, weeklyBest, score
	if kind == "update5" then
		mapID, level, weeklyBest, score = values[3], values[4], values[5], values[8]
	elseif kind == "sync6" and channel == "BNET" then
		mapID, level, weeklyBest, score = values[3], values[4], values[8], values[9]
	else
		mapID, level, weeklyBest, score = values[3], values[4], values[5], kind == "sync6" and values[8] or values[7]
	end
	return {
		name = name, classFile = classFile, mapID = tonumber(mapID), level = tonumber(level),
		weeklyBest = tonumber(weeklyBest), score = tonumber(score), faction = kind == "update5" and values[7] or nil,
		timestamp = time and time() or 0, source = "astral", scope = channel,
	}
end

function MythicKeys:ReceiveAstral(message, channel)
	if not SafeString(message) then return end
	local kind, payload = message:match("^(%S+)%s+(.+)$")
	if kind == "updateV9" or kind == "updateV8" or kind == "update5" then
		local entry = self:ParseAstralRecord(payload, kind, channel)
		if entry then self:MergeEntry(entry, false) end
	elseif kind == "sync6" then
		for record in payload:gmatch("([^_]+)") do
			local entry = self:ParseAstralRecord(record, kind, channel)
			if entry then self:MergeEntry(entry, false, true) end
		end
		self:RefreshDisplays()
	end
end

function MythicKeys:GetEntries()
	local entries = {}
	local cutoff = (time and time() or 0) - (self:GetDB().maxAgeDays or 21) * 86400
	for _, entry in pairs(self:GetDB().cache) do
		if type(entry) == "table" and (tonumber(entry.timestamp) or 0) >= cutoff then entries[#entries + 1] = entry end
	end
	table.sort(entries, function(left, right)
		if left.level ~= right.level then return left.level > right.level end
		return string.lower(left.name) < string.lower(right.name)
	end)
	return entries
end

function MythicKeys:GetBarText()
	local own = self:GetDB().cache[CacheKey(FullPlayerName())]
	if own and own.level > 0 then return string.format("Mythic + Keys: +%d %s", own.level, DungeonName(own.mapID)) end
	local count = 0
	for _, entry in ipairs(self:GetEntries()) do if entry.level > 0 then count = count + 1 end end
	return string.format("Mythic + Keys: %d known", count)
end

function MythicKeys:BuildTooltip(tooltip)
	local entries = self:GetEntries()
	tooltip:AddLine("Mythic + Keys", unpack(Theme.Colors.goldText))
	local shown = 0
	for _, entry in ipairs(entries) do
		if entry.level > 0 then
			shown = shown + 1
			if shown > 12 then break end
			local r, g, b = ClassColor(entry.classFile)
			tooltip:AddDoubleLine(ShortName(entry.name), string.format("+%d  %s", entry.level, DungeonName(entry.mapID)), r, g, b, 0.45, 0.90, 1.00)
		end
	end
	if shown == 0 then tooltip:AddLine("No current keystones are known yet.", 0.72, 0.75, 0.82, true) end
	if #entries > 12 then tooltip:AddLine(string.format("...and %d more", #entries - 12), 0.68, 0.71, 0.78) end
	tooltip:AddLine(" ")
	tooltip:AddLine("Reads SlickUI shares and compatible Astral Keys data.", 0.68, 0.71, 0.78, true)
end

local ScopeFilters = { "all", "self", "group", "guild", "friends", "astral" }
local ScopeLabels = { all = "All Sources", self = "This Character", group = "Party / Raid", guild = "Guild", friends = "Friends", astral = "Astral Keys" }
local MinimumLevels = { 0, 2, 5, 10, 15, 20 }

function MythicKeys:MatchesFilters(entry)
	local db = self:GetDB()
	local scope = db.scopeFilter or "all"
	if scope == "self" and entry.source ~= "self" then return false end
	if scope == "group" and entry.scope ~= "PARTY" and entry.scope ~= "RAID" then return false end
	if scope == "guild" and entry.scope ~= "GUILD" and entry.scope ~= "guild" then return false end
	if scope == "friends" and entry.scope ~= "WHISPER" and entry.scope ~= "BNET" and entry.scope ~= "friend" then return false end
	if scope == "astral" and entry.source ~= "astral" then return false end
	if entry.level < (db.minimumLevel or 0) then return false end
	local search = string.lower(db.search or "")
	if search ~= "" then
		local haystack = string.lower(table.concat({ entry.name or "", DungeonName(entry.mapID), SourceLabel(entry) }, " "))
		if not haystack:find(search, 1, true) then return false end
	end
	return true
end

function MythicKeys:BuildFiltered()
	wipe(self.Filtered)
	for _, entry in ipairs(self:GetEntries()) do if self:MatchesFilters(entry) then self.Filtered[#self.Filtered + 1] = entry end end
end

function MythicKeys:CreateWindow()
	if self.Window then return self.Window end
	local window = CreateFrame("Frame", "SlickUIMythicKeysFrame", UIParent, "BackdropTemplate")
	window:SetSize(850, 610)
	window:SetPoint("CENTER")
	window:SetFrameStrata("DIALOG")
	window:SetClampedToScreen(true)
	window:SetMovable(true)
	window:EnableMouse(true)
	window:RegisterForDrag("LeftButton")
	window:SetScript("OnDragStart", window.StartMoving)
	window:SetScript("OnDragStop", window.StopMovingOrSizing)
	Theme:CreatePanel(window)
	window:Hide()
	table.insert(UISpecialFrames, window:GetName())

	local title = Theme:CreateText(window, 20)
	title:SetPoint("TOPLEFT", 20, -17)
	title:SetText("Mythic + Keys")
	title:SetTextColor(unpack(Theme.Colors.goldText))
	local close = CreateFrame("Button", nil, window, "UIPanelCloseButton")
	close:SetPoint("TOPRIGHT", -4, -4)
	local summary = Theme:CreateText(window, 10)
	summary:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -7)
	window.Summary = summary

	local search = CreateFrame("EditBox", nil, window, "InputBoxTemplate")
	search:SetSize(250, 24)
	search:SetPoint("TOPLEFT", 20, -66)
	search:SetAutoFocus(false)
	search:SetText(self:GetDB().search or "")
	search:SetScript("OnTextChanged", function(box, userInput)
		if not userInput then return end
		MythicKeys:GetDB().search = box:GetText() or ""
		MythicKeys:RefreshWindow()
	end)
	window.Search = search
	local placeholder = Theme:CreateText(window, 9)
	placeholder:SetPoint("LEFT", search, "RIGHT", 8, 0)
	placeholder:SetText("Search player, dungeon, or source")
	placeholder:SetTextColor(unpack(Theme.Colors.mutedText))

	local scope = CreateFrame("Button", nil, window, "UIPanelButtonTemplate")
	scope:SetSize(150, 24)
	scope:SetPoint("TOPLEFT", 440, -66)
	scope:SetScript("OnClick", function()
		local current = MythicKeys:GetDB().scopeFilter or "all"
		local index = 1
		for i, value in ipairs(ScopeFilters) do if value == current then index = i break end end
		MythicKeys:GetDB().scopeFilter = ScopeFilters[index % #ScopeFilters + 1]
		MythicKeys:RefreshWindow()
	end)
	window.ScopeButton = scope
	local minimum = CreateFrame("Button", nil, window, "UIPanelButtonTemplate")
	minimum:SetSize(120, 24)
	minimum:SetPoint("LEFT", scope, "RIGHT", 10, 0)
	minimum:SetScript("OnClick", function()
		local current = MythicKeys:GetDB().minimumLevel or 0
		local index = 1
		for i, value in ipairs(MinimumLevels) do if value == current then index = i break end end
		MythicKeys:GetDB().minimumLevel = MinimumLevels[index % #MinimumLevels + 1]
		MythicKeys:RefreshWindow()
	end)
	window.MinimumButton = minimum
	local refresh = CreateFrame("Button", nil, window, "UIPanelButtonTemplate")
	refresh:SetSize(100, 24)
	refresh:SetPoint("LEFT", minimum, "RIGHT", 10, 0)
	refresh:SetText("Refresh")
	refresh:SetScript("OnClick", function()
		MythicKeys:ImportAstralCache()
		MythicKeys:RefreshOwnKey(true)
		MythicKeys:RefreshWindow()
	end)

	local header = CreateFrame("Frame", nil, window, "BackdropTemplate")
	header:SetPoint("TOPLEFT", 20, -106)
	header:SetPoint("TOPRIGHT", -20, -106)
	header:SetHeight(25)
	header:SetBackdrop({ bgFile = Theme.Texture })
	header:SetBackdropColor(unpack(Theme.Colors.panelSoft))
	local columns = { {"Player", 8, 165}, {"Dungeon", 178, 230}, {"Key", 413, 45}, {"Weekly", 465, 60}, {"Score", 532, 60}, {"Source", 600, 135}, {"Updated", 742, 70} }
	for _, info in ipairs(columns) do
		local text = Theme:CreateText(header, 10)
		text:SetPoint("LEFT", info[2], 0)
		text:SetText(info[1])
		text:SetTextColor(unpack(Theme.Colors.goldText))
	end

	local list = CreateFrame("Frame", nil, window)
	list:SetPoint("TOPLEFT", header, "BOTTOMLEFT", 0, -2)
	list:SetPoint("BOTTOMRIGHT", window, "BOTTOMRIGHT", -20, 38)
	list:EnableMouseWheel(true)
	list:SetScript("OnMouseWheel", function(_, delta)
		local maximum = math.max(0, #MythicKeys.Filtered - MythicKeys.VisibleRows)
		MythicKeys.ScrollOffset = math.max(0, math.min(maximum, MythicKeys.ScrollOffset - delta * 3))
		MythicKeys:RefreshWindowRows()
	end)
	window.Rows = {}
	for index = 1, self.VisibleRows do
		local row = CreateFrame("Frame", nil, list, "BackdropTemplate")
		row:SetPoint("TOPLEFT", 0, -(index - 1) * 29)
		row:SetPoint("TOPRIGHT", 0, -(index - 1) * 29)
		row:SetHeight(27)
		row:SetBackdrop({ bgFile = Theme.Texture })
		row:SetBackdropColor(index % 2 == 0 and 0.07 or 0.04, 0.05, 0.08, 0.72)
		row.Fields = {}
		for field, info in ipairs({ {8,165}, {178,230}, {413,45}, {465,60}, {532,60}, {600,135}, {742,70} }) do
			local text = Theme:CreateText(row, 9)
			text:SetPoint("LEFT", info[1], 0)
			text:SetWidth(info[2])
			text:SetJustifyH("LEFT")
			row.Fields[field] = text
		end
		window.Rows[index] = row
	end
	local hint = Theme:CreateText(window, 9)
	hint:SetPoint("BOTTOMLEFT", 20, 15)
	hint:SetText("Keys are shared by SlickUI and compatible Astral Keys users. Remote results reflect the newest received cache data.")
	hint:SetTextColor(unpack(Theme.Colors.mutedText))
	self.Window = window
	return window
end

function MythicKeys:RefreshWindowRows()
	if not self.Window then return end
	local maximum = math.max(0, #self.Filtered - self.VisibleRows)
	self.ScrollOffset = math.min(self.ScrollOffset, maximum)
	for index, row in ipairs(self.Window.Rows) do
		local entry = self.Filtered[self.ScrollOffset + index]
		row:SetShown(entry ~= nil)
		if entry then
			local r, g, b = ClassColor(entry.classFile)
			row.Fields[1]:SetText(entry.name)
			row.Fields[1]:SetTextColor(r, g, b)
			row.Fields[2]:SetText(DungeonName(entry.mapID))
			row.Fields[3]:SetText(entry.level > 0 and ("+" .. entry.level) or "--")
			row.Fields[3]:SetTextColor(entry.level > 0 and 0.45 or 0.68, entry.level > 0 and 0.90 or 0.71, entry.level > 0 and 1.00 or 0.78)
			row.Fields[4]:SetText(entry.weeklyBest > 0 and ("+" .. entry.weeklyBest) or "--")
			row.Fields[5]:SetText(entry.score > 0 and entry.score or "--")
			row.Fields[6]:SetText(SourceLabel(entry))
			row.Fields[7]:SetText(RelativeAge(entry.timestamp))
		end
	end
end

function MythicKeys:RefreshWindow()
	if not self.Window then return end
	self:BuildFiltered()
	local total, withKeys = #self.Filtered, 0
	for _, entry in ipairs(self.Filtered) do if entry.level > 0 then withKeys = withKeys + 1 end end
	self.Window.Summary:SetText(string.format("Known characters: %d    Current keystones: %d", total, withKeys))
	local scope = self:GetDB().scopeFilter or "all"
	self.Window.ScopeButton:SetText(ScopeLabels[scope] or "All Sources")
	local minimum = self:GetDB().minimumLevel or 0
	self.Window.MinimumButton:SetText(minimum > 0 and ("Minimum +" .. minimum) or "Any Key Level")
	self:RefreshWindowRows()
end

function MythicKeys:ShowWindow()
	local window = self:CreateWindow()
	self:ImportAstralCache()
	self:RefreshWindow()
	window:Show()
	window:Raise()
end

function MythicKeys:ToggleWindow()
	if self.Window and self.Window:IsShown() then self.Window:Hide() else self:ShowWindow() end
end

function MythicKeys:RefreshDisplays()
	if self.Window and self.Window:IsShown() then self:RefreshWindow() end
	if SlickUI.InformationBars then SlickUI.InformationBars:UpdateBlocks() end
end

function MythicKeys:Initialize()
	if C_ChatInfo and type(C_ChatInfo.RegisterAddonMessagePrefix) == "function" then
		pcall(C_ChatInfo.RegisterAddonMessagePrefix, self.Prefix)
		pcall(C_ChatInfo.RegisterAddonMessagePrefix, self.AstralPrefix)
	end
	local frame = CreateFrame("Frame")
	for _, event in ipairs({ "PLAYER_ENTERING_WORLD", "BAG_UPDATE_DELAYED", "CHALLENGE_MODE_START", "CHALLENGE_MODE_COMPLETED", "CHALLENGE_MODE_RESET", "GROUP_ROSTER_UPDATE", "GUILD_ROSTER_UPDATE", "CHAT_MSG_ADDON", "BN_CHAT_MSG_ADDON" }) do
		pcall(frame.RegisterEvent, frame, event)
	end
	frame:SetScript("OnEvent", function(_, event, ...)
		if event == "CHAT_MSG_ADDON" or event == "BN_CHAT_MSG_ADDON" then
			local prefix, message, channel = ...
			if prefix == MythicKeys.Prefix then
				local entry = MythicKeys:ParseNative(message, channel)
				if entry then MythicKeys:MergeEntry(entry, false) end
			elseif prefix == MythicKeys.AstralPrefix then
				MythicKeys:ReceiveAstral(message, event == "BN_CHAT_MSG_ADDON" and "BNET" or channel)
			end
			return
		end
		C_Timer.After(event == "PLAYER_ENTERING_WORLD" and 3 or 0.6, function()
			MythicKeys:ImportAstralCache()
			MythicKeys:RefreshOwnKey(event == "PLAYER_ENTERING_WORLD" or event == "GROUP_ROSTER_UPDATE" or event == "GUILD_ROSTER_UPDATE")
		end)
	end)
	self.Driver = frame
	C_Timer.After(3, function()
		MythicKeys:ImportAstralCache()
		MythicKeys:RefreshOwnKey(true)
	end)
end

SlickUI:RegisterModule("MythicKeys", MythicKeys)
