local ADDON_NAME, namespace = ...
local GuildPing = namespace.GuildPing
local UI = GuildPing.UIFactory

if not UI then return end

local CreateWindow = UI.CreateWindow
local MakeDraggable = UI.MakeDraggable
local SetTitle = UI.SetTitle
local CreateButton = UI.CreateButton
local CreateLabel = UI.CreateLabel
local ROW_BACKDROP = UI.RowBackdrop
local GUILD_GREEN = UI.GuildGreen
local MUTED_COLOR = UI.MutedColor
local WARNING_GOLD = UI.WarningGold
local UPDATE_RED = UI.UpdateRed
local Unpack = unpack or table.unpack

local RESTART_POPUP_KEY = "GUILD_PING_RUNE_MASTERMIND_RESTART"
local PANEL_DARK = { 0.045, 0.050, 0.075, 0.98 }
local RUNE_BORDER = { 0.26, 0.29, 0.42, 1 }
local RUNE_GOLD = { 0.95, 0.72, 0.22, 1 }
local RUNE_TEXTURE = "Interface\\AddOns\\" .. ADDON_NAME .. "\\Media\\RuneMastermind-Runes.png"
local RUNE_COLORS = {
	SUN = { 0.82, 0.58, 0.10, 1 },
	MOON = { 0.34, 0.66, 0.94, 1 },
	LEAF = { 0.14, 0.68, 0.35, 1 },
	FLAME = { 0.88, 0.28, 0.16, 1 },
	TIDE = { 0.08, 0.68, 0.72, 1 },
	VOID = { 0.58, 0.34, 0.82, 1 },
}
local RUNE_TEX_COORDS = {
	SUN = { 82 / 1536, 486 / 1536, 63 / 1024, 467 / 1024 },
	MOON = { 566 / 1536, 970 / 1536, 63 / 1024, 467 / 1024 },
	LEAF = { 1038 / 1536, 1442 / 1536, 63 / 1024, 467 / 1024 },
	FLAME = { 82 / 1536, 486 / 1536, 548 / 1024, 952 / 1024 },
	TIDE = { 566 / 1536, 970 / 1536, 548 / 1024, 952 / 1024 },
	VOID = { 1038 / 1536, 1442 / 1536, 548 / 1024, 952 / 1024 },
}

local function GetRune(runeID)
	for _, rune in ipairs(GuildPing:GetRuneMastermindRunes()) do
		if rune.id == runeID then return rune end
	end
end

local function CreateRuneToken(parent, size, clickable)
	local token = CreateFrame(clickable and "Button" or "Frame", nil, parent, "BackdropTemplate")
	token:SetSize(size, size)
	token:SetBackdrop(ROW_BACKDROP)
	token:SetBackdropColor(Unpack(PANEL_DARK))
	token:SetBackdropBorderColor(Unpack(RUNE_BORDER))
	token.icon = token:CreateTexture(nil, "ARTWORK")
	token.icon:SetPoint("TOPLEFT", 2, -2)
	token.icon:SetPoint("BOTTOMRIGHT", -2, 2)
	token.icon:SetTexture(RUNE_TEXTURE)
	token.icon:Hide()
	token.showCode = size >= 38
	token.glyph = CreateLabel(token, "?", token.showCode and "GameFontNormalSmall" or "GameFontHighlightSmall")
	token.glyph:SetPoint("CENTER", 0, 1)
	token.glyph:SetTextColor(1, 1, 1, 1)
	return token
end

local function SetRuneToken(token, runeID, selected)
	local rune = runeID and GetRune(runeID) or nil
	if rune then
		local texCoords = RUNE_TEX_COORDS[rune.id]
		token:SetBackdropColor(Unpack(PANEL_DARK))
		token.icon:SetTexCoord(Unpack(texCoords))
		token.icon:Show()
		token.glyph:ClearAllPoints()
		token.glyph:SetPoint("BOTTOMRIGHT", -3, 3)
		token.glyph:SetText(rune.glyph)
		token.glyph:SetShown(token.showCode)
	else
		token:SetBackdropColor(Unpack(PANEL_DARK))
		token.icon:Hide()
		token.glyph:ClearAllPoints()
		token.glyph:SetPoint("CENTER", 0, 1)
		token.glyph:SetText("?")
		token.glyph:Show()
	end
	token:SetBackdropBorderColor(Unpack(selected and RUNE_GOLD or RUNE_BORDER))
end

local function HasCurrentGuess(game)
	for index = 1, GuildPing.RUNE_MASTERMIND_CODE_LENGTH do
		if game.currentGuess[index] then return true end
	end
	return false
end

local function EnsurePopup()
	if StaticPopupDialogs[RESTART_POPUP_KEY] then return end
	StaticPopupDialogs[RESTART_POPUP_KEY] = {
		text = "Start a new Rune Mastermind puzzle? The current secret and guesses will be replaced.",
		button1 = "New Puzzle", button2 = CANCEL, timeout = 0, whileDead = true,
		hideOnEscape = true, preferredIndex = 3,
		OnAccept = function() GuildPing:StartRuneMastermindGame() end,
	}
end

function GuildPing:CreateRuneMastermindFrame()
	if self.runeMastermindFrame then return self.runeMastermindFrame end
	EnsurePopup()
	local frame = CreateWindow("GuildPingRuneMastermindFrame")
	frame:SetSize(760, 760)
	frame:SetPoint("CENTER")
	frame:SetFrameStrata("DIALOG")
	MakeDraggable(frame, frame.header)
	SetTitle(frame, "Guild Ping — Rune Mastermind")

	frame.heading = CreateLabel(frame, "Crack the rune lock", "GameFontNormalLarge")
	frame.heading:SetPoint("TOPLEFT", 24, -50)
	frame.attemptCounter = CreateLabel(frame, "", "GameFontHighlight")
	frame.attemptCounter:SetPoint("TOPRIGHT", -24, -52)
	frame.description = CreateLabel(frame, "Discover four hidden runes. Repeats are allowed; every rune has a unique shape and letter.", "GameFontHighlight")
	frame.description:SetPoint("TOPLEFT", 24, -80)
	frame.description:SetTextColor(MUTED_COLOR:GetRGB())

	frame.secretPanel = CreateFrame("Frame", nil, frame, "BackdropTemplate")
	frame.secretPanel:SetPoint("TOPLEFT", 24, -108)
	frame.secretPanel:SetPoint("TOPRIGHT", -24, -108)
	frame.secretPanel:SetHeight(88)
	frame.secretPanel:SetBackdrop(ROW_BACKDROP)
	frame.secretPanel:SetBackdropColor(Unpack(PANEL_DARK))
	frame.secretPanel:SetBackdropBorderColor(0.25, 0.18, 0.38, 1)
	frame.secretLabel = CreateLabel(frame.secretPanel, "HIDDEN LOCK", "GameFontNormalSmall")
	frame.secretLabel:SetPoint("LEFT", 22, 0)
	frame.secretLabel:SetTextColor(Unpack(RUNE_GOLD))
	frame.secretSlots = {}
	for index = 1, 4 do
		local token = CreateRuneToken(frame.secretPanel, 52, false)
		token:SetPoint("LEFT", 252 + ((index - 1) * 70), 0)
		frame.secretSlots[index] = token
	end

	frame.currentLabel = CreateLabel(frame, "YOUR GUESS", "GameFontNormalSmall")
	frame.currentLabel:SetPoint("TOPLEFT", 24, -214)
	frame.currentLabel:SetTextColor(Unpack(RUNE_GOLD))
	frame.currentSlots = {}
	for index = 1, 4 do
		local token = CreateRuneToken(frame, 60, true)
		token:SetPoint("TOPLEFT", 235 + ((index - 1) * 72), -205)
		token.slotIndex = index
		token:SetScript("OnClick", function(self)
			GuildPing:CycleRuneMastermindSlot(self.slotIndex)
		end)
		token:SetScript("OnEnter", function(self)
			if not GameTooltip then return end
			GameTooltip:SetOwner(self, "ANCHOR_TOP")
			GameTooltip:SetText("Rune slot " .. self.slotIndex)
			GameTooltip:AddLine("Click to cycle this slot, or choose a rune below.", 0.78, 0.82, 0.92, true)
			GameTooltip:Show()
		end)
		token:SetScript("OnLeave", function() if GameTooltip then GameTooltip:Hide() end end)
		frame.currentSlots[index] = token
	end

	frame.paletteLabel = CreateLabel(frame, "CHOOSE A RUNE", "GameFontNormalSmall")
	frame.paletteLabel:SetPoint("TOPLEFT", 24, -291)
	frame.paletteLabel:SetTextColor(Unpack(RUNE_GOLD))
	frame.paletteButtons = {}
	for index, rune in ipairs(self:GetRuneMastermindRunes()) do
		local button = CreateFrame("Button", nil, frame, "BackdropTemplate")
		button:SetSize(100, 48)
		button:SetPoint("TOPLEFT", 60 + ((index - 1) * 108), -312)
		button:SetBackdrop(ROW_BACKDROP)
		button:SetBackdropColor(Unpack(RUNE_COLORS[rune.id]))
		button:SetBackdropBorderColor(Unpack(RUNE_BORDER))
		button.runeID = rune.id
		button.icon = button:CreateTexture(nil, "ARTWORK")
		button.icon:SetSize(42, 42)
		button.icon:SetPoint("LEFT", 3, 0)
		button.icon:SetTexture(RUNE_TEXTURE)
		button.icon:SetTexCoord(Unpack(RUNE_TEX_COORDS[rune.id]))
		button.nameText = CreateLabel(button, rune.label, "GameFontHighlightSmall")
		button.nameText:SetPoint("LEFT", 48, 7)
		button.codeText = CreateLabel(button, rune.glyph, "GameFontNormalSmall")
		button.codeText:SetPoint("LEFT", 48, -9)
		button.codeText:SetTextColor(MUTED_COLOR:GetRGB())
		button:SetScript("OnClick", function(self)
			local game = GuildPing.activeRuneMastermindGame
			local changed, errorMessage = GuildPing:SetRuneMastermindGuess(game and game.selectedSlot or 1, self.runeID)
			if not changed then GuildPing:Print(errorMessage) end
		end)
		button:SetScript("OnEnter", function(self) if self:IsEnabled() then self:SetBackdropBorderColor(Unpack(RUNE_GOLD)) end end)
		button:SetScript("OnLeave", function(self) self:SetBackdropBorderColor(Unpack(RUNE_BORDER)) end)
		frame.paletteButtons[index] = button
	end

	frame.feedbackHelp = CreateLabel(frame, "Exact = right rune and slot  •  Misplaced = right rune, different slot", "GameFontHighlightSmall")
	frame.feedbackHelp:SetPoint("TOP", 0, -374)
	frame.feedbackHelp:SetTextColor(MUTED_COLOR:GetRGB())
	frame.clearButton = CreateButton(frame, "Clear", 94, 27)
	frame.clearButton:SetPoint("TOP", -56, -401)
	frame.clearButton:SetScript("OnClick", function() GuildPing:ClearRuneMastermindGuess() end)
	frame.submitButton = CreateButton(frame, "Submit Guess", 136, 27, "primary")
	frame.submitButton:SetPoint("LEFT", frame.clearButton, "RIGHT", 10, 0)
	frame.submitButton:SetScript("OnClick", function()
		local attempt, errorMessage = GuildPing:SubmitRuneMastermindGuess()
		if not attempt then GuildPing:Print(errorMessage) end
	end)
	frame.statusText = CreateLabel(frame, "", "GameFontNormal")
	frame.statusText:SetPoint("TOP", 0, -440)

	frame.historyLabel = CreateLabel(frame, "ATTEMPTS", "GameFontNormalSmall")
	frame.historyLabel:SetPoint("TOPLEFT", 24, -469)
	frame.historyLabel:SetTextColor(Unpack(RUNE_GOLD))
	frame.historyRows = {}
	for index = 1, self.RUNE_MASTERMIND_MAX_ATTEMPTS do
		local row = CreateFrame("Frame", nil, frame, "BackdropTemplate")
		row:SetSize(712, 25)
		row:SetPoint("TOPLEFT", 24, -489 - ((index - 1) * 27))
		row:SetBackdrop(ROW_BACKDROP)
		row:SetBackdropColor(0.047, 0.055, 0.078, index % 2 == 0 and 0.78 or 0.94)
		row:SetBackdropBorderColor(0.13, 0.15, 0.21, 1)
		row.numberText = CreateLabel(row, tostring(index), "GameFontHighlightSmall")
		row.numberText:SetPoint("LEFT", 10, 0)
		row.numberText:SetTextColor(MUTED_COLOR:GetRGB())
		row.tokens = {}
		for slot = 1, 4 do
			local token = CreateRuneToken(row, 19, false)
			token:SetPoint("LEFT", 44 + ((slot - 1) * 27), 0)
			row.tokens[slot] = token
		end
		row.feedbackText = CreateLabel(row, "", "GameFontHighlightSmall")
		row.feedbackText:SetPoint("LEFT", 170, 0)
		frame.historyRows[index] = row
	end

	frame.gamesButton = CreateButton(frame, "All Games", 104, 25)
	frame.gamesButton:SetPoint("BOTTOMLEFT", 24, 16)
	frame.gamesButton:SetScript("OnClick", function() frame:Hide() GuildPing:ShowGames() end)
	frame.newGameButton = CreateButton(frame, "New Puzzle", 124, 25, "primary")
	frame.newGameButton:SetPoint("BOTTOM", 0, 16)
	frame.newGameButton:SetScript("OnClick", function()
		local game = GuildPing.activeRuneMastermindGame
		if game and game.status == "ACTIVE" and (#game.attempts > 0 or HasCurrentGuess(game)) then StaticPopup_Show(RESTART_POPUP_KEY)
		else GuildPing:StartRuneMastermindGame() end
	end)
	frame.closeButton = CreateButton(frame, "Close", 90, 25)
	frame.closeButton:SetPoint("BOTTOMRIGHT", -24, 16)
	frame.closeButton:SetScript("OnClick", function() frame:Hide() end)
	frame:SetScript("OnShow", function() GuildPing:RefreshRuneMastermindFrame() end)
	frame:Hide()
	self.runeMastermindFrame = frame
	return frame
end

function GuildPing:RefreshRuneMastermindFrame()
	local frame, game = self.runeMastermindFrame, self.activeRuneMastermindGame
	if not frame then return end
	if not game then frame:Hide() return end
	frame.attemptCounter:SetText(game.status == "FINISHED"
		and string.format("%d attempt%s used", #game.attempts, #game.attempts == 1 and "" or "s")
		or string.format("Attempt %d of %d", #game.attempts + 1, game.maxAttempts))
	for index, token in ipairs(frame.secretSlots) do SetRuneToken(token, game.status == "FINISHED" and game.secret[index] or nil, false) end
	frame.secretLabel:SetText(game.status == "FINISHED" and "RUNE LOCK REVEALED" or "HIDDEN LOCK")
	for index, token in ipairs(frame.currentSlots) do
		SetRuneToken(token, game.currentGuess[index], game.status == "ACTIVE" and game.selectedSlot == index)
		token:SetEnabled(game.status == "ACTIVE")
	end
	for _, button in ipairs(frame.paletteButtons) do button:SetEnabled(game.status == "ACTIVE") end
	local completeGuess = true
	for index = 1, self.RUNE_MASTERMIND_CODE_LENGTH do
		if not game.currentGuess[index] then completeGuess = false break end
	end
	frame.clearButton:SetShown(game.status == "ACTIVE")
	frame.clearButton:SetEnabled(HasCurrentGuess(game))
	frame.submitButton:SetShown(game.status == "ACTIVE")
	frame.submitButton:SetEnabled(completeGuess)
	if game.status == "FINISHED" and game.won then
		frame.statusText:SetText(string.format("Lock opened in %d attempt%s!", #game.attempts, #game.attempts == 1 and "" or "s"))
		frame.statusText:SetTextColor(GUILD_GREEN:GetRGB())
	elseif game.status == "FINISHED" then
		frame.statusText:SetText("The rune lock held. Study the revealed code and try again.")
		frame.statusText:SetTextColor(UPDATE_RED:GetRGB())
	elseif #game.attempts > 0 then
		local latest = game.attempts[#game.attempts]
		frame.statusText:SetText(string.format("Last guess: %d exact • %d misplaced", latest.exact, latest.misplaced))
		frame.statusText:SetTextColor(WARNING_GOLD:GetRGB())
	else
		frame.statusText:SetText("Choose a rune for each slot, then submit your guess.")
		frame.statusText:SetTextColor(MUTED_COLOR:GetRGB())
	end
	for index, row in ipairs(frame.historyRows) do
		local attempt = game.attempts[index]
		for slot, token in ipairs(row.tokens) do SetRuneToken(token, attempt and attempt.guess[slot] or nil, false) end
		row.feedbackText:SetText(attempt and string.format("%d exact  •  %d misplaced", attempt.exact, attempt.misplaced) or "")
		if attempt then
			row.feedbackText:SetTextColor((attempt.exact == self.RUNE_MASTERMIND_CODE_LENGTH and GUILD_GREEN or UI.TextColor):GetRGB())
		end
	end
	frame.newGameButton:SetText(game.status == "FINISHED" and "Play Again" or "New Puzzle")
end

function GuildPing:ShowRuneMastermind()
	if not self.activeRuneMastermindGame then self:StartRuneMastermindGame() end
	if self.gamesHub then self.gamesHub:Hide() end
	if self.checkersLobby then self.checkersLobby:Hide() end
	if self.checkersBoard then self.checkersBoard:Hide() end
	if self.deathRollLobby then self.deathRollLobby:Hide() end
	if self.deathRollGameFrame then self.deathRollGameFrame:Hide() end
	if self.pokerLobby then self.pokerLobby:Hide() end
	if self.pokerTableFrame then self.pokerTableFrame:Hide() end
	if self.connectFourLobby then self.connectFourLobby:Hide() end
	if self.connectFourBoard then self.connectFourBoard:Hide() end
	if self.fiveDiceFrame then self.fiveDiceFrame:Hide() end
	if self.spectatorLobby then self.spectatorLobby:Hide() end
	if self.spectatorGameWindow then self.spectatorGameWindow:Hide() end
	local frame = self:CreateRuneMastermindFrame()
	frame:Show()
	self:RefreshRuneMastermindFrame()
end
