local ADDON_NAME = ...

local SSG = {}
_G.SoloShuffleGuard = SSG

local DEFAULTS = {
	enableSound = true,
	enableRaidWarning = true,
	enableChat = true,
}

local QUEUE_STATUS_ACTIVE = {
	queued = true,
	confirm = true,
	active = true,
}

local SOLO_SHUFFLE_PATTERNS = {
	"solo shuffle",
	"shuffle",
}

local POP_TEXT = "Solo Shuffle popped while you have another party queue active. Taking it may disrupt your party queue."

local eventFrame = CreateFrame("Frame")
local popFrame
local lastSnapshot = {
	soloConfirm = false,
	hasPartyQueue = false,
}

local function CopyDefaults(source, target)
	target = target or {}
	for key, value in pairs(source) do
		if target[key] == nil then
			target[key] = value
		end
	end
	return target
end

local function IsInPlayerParty()
	return IsInGroup() or UnitInParty("player")
end

local function Print(message)
	if SoloShuffleGuardDB and SoloShuffleGuardDB.enableChat then
		DEFAULT_CHAT_FRAME:AddMessage("|cff50c8ffSolo Shuffle Guard:|r " .. message)
	end
end

local function PlayWarningSound()
	if SoloShuffleGuardDB and SoloShuffleGuardDB.enableSound and SOUNDKIT then
		PlaySound(SOUNDKIT.RAID_WARNING, "Master")
	end
end

local function RaidWarn(message)
	if SoloShuffleGuardDB and SoloShuffleGuardDB.enableRaidWarning and RaidNotice_AddMessage and RaidWarningFrame then
		RaidNotice_AddMessage(RaidWarningFrame, message, ChatTypeInfo.RAID_WARNING or ChatTypeInfo.SYSTEM)
	end
end

local function Normalize(value)
	return value and tostring(value):lower() or ""
end

local function TextLooksLikeSoloShuffle(...)
	local text = ""
	for index = 1, select("#", ...) do
		text = text .. " " .. Normalize(select(index, ...))
	end

	for _, pattern in ipairs(SOLO_SHUFFLE_PATTERNS) do
		if text:find(pattern, 1, true) then
			return true
		end
	end

	return false
end

local function GetQueueInfoFromC(index)
	if not C_PvP or not C_PvP.GetQueueStatus then
		return nil
	end

	local first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth, eleventh = C_PvP.GetQueueStatus(index)
	if type(first) == "table" then
		local data = first
		return {
			index = index,
			battlefieldIndex = data.battlefieldIndex or data.index,
			status = data.status,
			name = data.mapName or data.name,
			teamSize = data.teamSize,
			registeredMatch = data.registeredMatch,
			suspendedQueue = data.suspendedQueue,
			queueType = data.queueType,
			gameType = data.gameType,
			role = data.role,
			asGroup = data.asGroup,
			shortDescription = data.shortDescription,
			longDescription = data.longDescription,
			isSoloQueue = data.isSoloQueue,
			bracket = data.bracket,
			matchType = data.matchType,
		}
	end

	if first then
		return {
			index = index,
			battlefieldIndex = index,
			status = first,
			name = second,
			teamSize = third,
			registeredMatch = fourth,
			suspendedQueue = fifth,
			queueType = sixth,
			gameType = seventh,
			role = eighth,
			asGroup = ninth,
			shortDescription = tenth,
			longDescription = eleventh,
		}
	end

	return nil
end

local function GetQueueInfoFromLegacy(index)
	if not GetBattlefieldStatus then
		return nil
	end

	local status, mapName, teamSize, registeredMatch, suspendedQueue, queueType, gameType, role, asGroup, shortDescription, longDescription, isSoloQueue = GetBattlefieldStatus(index)
	if not status or status == "none" then
		return nil
	end

	return {
		index = index,
		battlefieldIndex = index,
		status = status,
		name = mapName,
		teamSize = teamSize,
		registeredMatch = registeredMatch,
		suspendedQueue = suspendedQueue,
		queueType = queueType,
		gameType = gameType,
		role = role,
		asGroup = asGroup,
		shortDescription = shortDescription,
		longDescription = longDescription,
		isSoloQueue = isSoloQueue,
	}
end

local function GetQueueInfo(index)
	local info = GetQueueInfoFromLegacy(index) or GetQueueInfoFromC(index)
	if not info or not info.status or info.status == "none" then
		return nil
	end

	return info
end

local function HasEnumValue(enumTable, key, value)
	return enumTable and enumTable[key] ~= nil and enumTable[key] == value
end

local function IsSoloShuffleQueue(info)
	if not info then
		return false
	end

	if info.isSoloQueue and TextLooksLikeSoloShuffle(info.name, info.shortDescription, info.longDescription, info.queueType, info.gameType) then
		return true
	end

	if HasEnumValue(Enum and Enum.PvPMatchType, "SoloShuffle", info.matchType) then
		return true
	end

	if HasEnumValue(Enum and Enum.PvPBracket, "SoloShuffle", info.bracket) then
		return true
	end

	return TextLooksLikeSoloShuffle(info.name, info.shortDescription, info.longDescription, info.queueType, info.gameType)
end

local function IsPartyQueue(info)
	if not info or IsSoloShuffleQueue(info) then
		return false
	end

	if info.asGroup then
		return true
	end

	return IsInPlayerParty()
end

local function IterateQueues(callback)
	local maxQueues = (GetMaxBattlefieldID and GetMaxBattlefieldID()) or MAX_BATTLEFIELD_QUEUES or 4
	for index = 1, maxQueues do
		local info = GetQueueInfo(index)
		if info and QUEUE_STATUS_ACTIVE[info.status] then
			callback(info)
		end
	end
end

local function GetQueueSnapshot()
	local snapshot = {
		hasSoloShuffle = false,
		hasPartyQueue = false,
		soloConfirm = false,
		soloIndex = nil,
		partyIndex = nil,
		soloName = nil,
		partyName = nil,
	}

	IterateQueues(function(info)
		if IsSoloShuffleQueue(info) then
			snapshot.hasSoloShuffle = true
			snapshot.soloIndex = info.battlefieldIndex or info.index
			snapshot.soloName = info.name or "Solo Shuffle"
			if info.status == "confirm" then
				snapshot.soloConfirm = true
			end
		elseif IsPartyQueue(info) then
			snapshot.hasPartyQueue = true
			snapshot.partyIndex = info.index
			snapshot.partyName = info.name or "party queue"
		end
	end)

	return snapshot
end

local function CreatePanel(name, title, width, height)
	local frame = CreateFrame("Frame", name, UIParent, "BasicFrameTemplateWithInset")
	frame:SetSize(width, height)
	frame:SetPoint("CENTER")
	frame:SetFrameStrata("DIALOG")
	frame:SetMovable(true)
	frame:EnableMouse(true)
	frame:RegisterForDrag("LeftButton")
	frame:SetScript("OnDragStart", frame.StartMoving)
	frame:SetScript("OnDragStop", frame.StopMovingOrSizing)
	frame:Hide()

	if frame.TitleText then
		frame.TitleText:SetText(title)
	end

	frame.Message = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
	frame.Message:SetPoint("TOPLEFT", 24, -52)
	frame.Message:SetPoint("BOTTOMRIGHT", -24, 64)
	frame.Message:SetJustifyH("CENTER")
	frame.Message:SetJustifyV("MIDDLE")
	frame.Message:SetSpacing(4)

	return frame
end

local function CreateButton(parent, text, width, point, onClick)
	local button = CreateFrame("Button", nil, parent, "UIPanelButtonTemplate")
	button:SetSize(width, 24)
	button:SetPoint(unpack(point))
	button:SetText(text)
	button:SetScript("OnClick", onClick)
	return button
end

local function EnsurePopFrame()
	if popFrame then
		return popFrame
	end

	popFrame = CreatePanel(ADDON_NAME .. "PopFrame", "Solo Shuffle Guard", 480, 190)
	popFrame.Message:SetText(POP_TEXT)

	popFrame.CloseButton = CreateButton(popFrame, "Dismiss", 110, { "BOTTOM", 0, 18 }, function()
		popFrame:Hide()
	end)

	return popFrame
end

local function ShowPopPanel(snapshot)
	local frame = EnsurePopFrame()
	frame.soloIndex = snapshot.soloIndex
	frame:Show()
	PlayWarningSound()
	RaidWarn(POP_TEXT)
	Print(POP_TEXT)
end

local function EvaluateQueues()
	local snapshot = GetQueueSnapshot()
	local conflictActive = snapshot.soloConfirm and snapshot.hasPartyQueue
	local conflictStarted = conflictActive and not (lastSnapshot.soloConfirm and lastSnapshot.hasPartyQueue)

	if conflictStarted then
		ShowPopPanel(snapshot)
	end

	if not snapshot.soloConfirm and popFrame then
		popFrame:Hide()
	end

	lastSnapshot = snapshot
end

function SSG.Status()
	local snapshot = GetQueueSnapshot()
	if snapshot.hasSoloShuffle then
		Print("Solo Shuffle queue detected" .. (snapshot.soloConfirm and " and it is popped." or "."))
	else
		Print("No Solo Shuffle queue detected.")
	end

	if snapshot.hasPartyQueue then
		Print("Party queue detected: " .. (snapshot.partyName or "unknown"))
	else
		Print("No party queue detected.")
	end
end

function SSG.TestWarning()
	ShowPopPanel({ soloIndex = nil })
end

function SSG.Debug()
	local found = false
	IterateQueues(function(info)
		found = true
		Print(string.format(
			"slot=%s battlefieldSlot=%s status=%s name=%s queueType=%s gameType=%s asGroup=%s isSoloQueue=%s",
			tostring(info.index),
			tostring(info.battlefieldIndex),
			tostring(info.status),
			tostring(info.name),
			tostring(info.queueType),
			tostring(info.gameType),
			tostring(info.asGroup),
			tostring(info.isSoloQueue)
		))
	end)

	if not found then
		Print("No active battlefield queue slots found.")
	end
end

SLASH_SOLOSHUFFLEGUARD1 = "/ssg"
SLASH_SOLOSHUFFLEGUARD2 = "/soloshuffleguard"
SlashCmdList.SOLOSHUFFLEGUARD = function(input)
	input = Normalize(input)
	if input == "test" then
		SSG.TestWarning()
	elseif input == "status" then
		SSG.Status()
	elseif input == "debug" then
		SSG.Debug()
	elseif input == "sound" then
		SoloShuffleGuardDB.enableSound = not SoloShuffleGuardDB.enableSound
		Print("Sound warnings " .. (SoloShuffleGuardDB.enableSound and "enabled." or "disabled."))
	elseif input == "raid" then
		SoloShuffleGuardDB.enableRaidWarning = not SoloShuffleGuardDB.enableRaidWarning
		Print("Raid warning text " .. (SoloShuffleGuardDB.enableRaidWarning and "enabled." or "disabled."))
	else
		Print("/ssg status, /ssg debug, /ssg test, /ssg sound, /ssg raid")
	end
end

local function RegisterEvent(event)
	pcall(eventFrame.RegisterEvent, eventFrame, event)
end

RegisterEvent("ADDON_LOADED")
RegisterEvent("PLAYER_ENTERING_WORLD")
RegisterEvent("GROUP_ROSTER_UPDATE")
RegisterEvent("UPDATE_BATTLEFIELD_STATUS")
RegisterEvent("PVPQUEUE_ANYWHERE_UPDATE")
RegisterEvent("LFG_QUEUE_STATUS_UPDATE")

eventFrame:SetScript("OnEvent", function(_, event, addonName)
	if event == "ADDON_LOADED" and addonName == ADDON_NAME then
		SoloShuffleGuardDB = CopyDefaults(DEFAULTS, SoloShuffleGuardDB)
		C_Timer.After(1, EvaluateQueues)
	elseif event ~= "ADDON_LOADED" then
		C_Timer.After(0.15, EvaluateQueues)
	end
end)
