local _, 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_FIVE_DICE_RESTART"
local DICE_AMBER = { 0.96, 0.57, 0.16, 1 }
local DICE_CREAM = { 1.00, 0.93, 0.76, 1 }
local TRAY_DARK = { 0.055, 0.045, 0.050, 1 }
local ROW_DARK = { 0.047, 0.055, 0.078, 0.96 }
local ROW_USED = { 0.070, 0.082, 0.110, 0.82 }

local function CreateScoreRow(parent, category, width)
	local row = CreateFrame("Button", nil, parent, "BackdropTemplate")
	row:SetSize(width, 32)
	row:SetBackdrop(ROW_BACKDROP)
	row:SetBackdropColor(Unpack(ROW_DARK))
	row:SetBackdropBorderColor(0.20, 0.23, 0.31, 1)
	row.categoryKey = category.key
	row.nameText = CreateLabel(row, category.label, "GameFontHighlight")
	row.nameText:SetPoint("LEFT", 11, 0)
	row.scoreText = CreateLabel(row, "", "GameFontNormal")
	row.scoreText:SetPoint("RIGHT", -11, 0)
	row:SetScript("OnClick", function(self)
		local score, errorMessage = GuildPing:ScoreFiveDiceCategory(self.categoryKey)
		if score == nil then GuildPing:Print(errorMessage) end
	end)
	row:SetScript("OnEnter", function(self)
		if self.used or self.disabledByAnimation then return end
		self:SetBackdropColor(0.11, 0.13, 0.19, 1)
		self:SetBackdropBorderColor(Unpack(DICE_AMBER))
	end)
	row:SetScript("OnLeave", function(self)
		self:SetBackdropColor(Unpack(self.used and ROW_USED or ROW_DARK))
		self:SetBackdropBorderColor(Unpack(self.used and { 0.16, 0.18, 0.23, 1 } or { 0.20, 0.23, 0.31, 1 }))
	end)
	return row
end

local function EnsurePopup()
	if StaticPopupDialogs[RESTART_POPUP_KEY] then return end
	StaticPopupDialogs[RESTART_POPUP_KEY] = {
		text = "Start a new Five Dice game? Your current scorecard will be replaced.",
		button1 = "New Game", button2 = CANCEL, timeout = 0, whileDead = true,
		hideOnEscape = true, preferredIndex = 3,
		OnAccept = function() GuildPing:StartFiveDiceGame() end,
	}
end

function GuildPing:CreateFiveDiceFrame()
	if self.fiveDiceFrame then return self.fiveDiceFrame end
	EnsurePopup()
	local frame = CreateWindow("GuildPingFiveDiceFrame")
	frame:SetSize(860, 760)
	frame:SetPoint("CENTER")
	frame:SetFrameStrata("DIALOG")
	MakeDraggable(frame, frame.header)
	SetTitle(frame, "Guild Ping — Five Dice")

	frame.heading = CreateLabel(frame, "Five Dice", "GameFontNormalLarge")
	frame.heading:SetPoint("TOPLEFT", 24, -50)
	frame.roundText = CreateLabel(frame, "", "GameFontHighlight")
	frame.roundText:SetPoint("TOPRIGHT", -24, -52)
	frame.description = CreateLabel(frame, "Roll up to three times, hold the dice you want, then choose one score.", "GameFontHighlight")
	frame.description:SetPoint("TOPLEFT", 24, -80)
	frame.description:SetTextColor(MUTED_COLOR:GetRGB())

	frame.tray = CreateFrame("Frame", nil, frame, "BackdropTemplate")
	frame.tray:SetPoint("TOPLEFT", 24, -110)
	frame.tray:SetPoint("TOPRIGHT", -24, -110)
	frame.tray:SetHeight(132)
	frame.tray:SetBackdrop(ROW_BACKDROP)
	frame.tray:SetBackdropColor(Unpack(TRAY_DARK))
	frame.tray:SetBackdropBorderColor(0.43, 0.27, 0.10, 1)
	frame.dice = {}
	for index = 1, 5 do
		local die = CreateFrame("Button", nil, frame.tray, "BackdropTemplate")
		die:SetSize(92, 92)
		die:SetPoint("TOPLEFT", 140 + ((index - 1) * 112), -16)
		die:SetBackdrop(ROW_BACKDROP)
		die:SetBackdropColor(0.18, 0.11, 0.055, 1)
		die:SetBackdropBorderColor(Unpack(DICE_AMBER))
		die.index = index
		die.valueText = CreateLabel(die, "–", "GameFontNormalHuge")
		die.valueText:SetPoint("CENTER", 0, 4)
		die.valueText:SetTextColor(Unpack(DICE_CREAM))
		die.holdText = CreateLabel(die, "", "GameFontNormalSmall")
		die.holdText:SetPoint("BOTTOM", 0, 8)
		die.holdText:SetTextColor(GUILD_GREEN:GetRGB())
		die:SetScript("OnClick", function(self)
			local held, errorMessage = GuildPing:ToggleFiveDiceHold(self.index)
			if held == nil then GuildPing:Print(errorMessage) end
		end)
		die:SetScript("OnEnter", function(self)
			if self:IsEnabled() then self:SetBackdropBorderColor(GUILD_GREEN:GetRGB()) end
		end)
		die:SetScript("OnLeave", function(self)
			local game = GuildPing.activeFiveDiceGame
			self:SetBackdropBorderColor(Unpack(game and game.held[self.index] and { 0.20, 0.82, 0.40, 1 } or DICE_AMBER))
		end)
		frame.dice[index] = die
	end

	frame.statusText = CreateLabel(frame, "", "GameFontNormal")
	frame.statusText:SetPoint("TOP", frame.tray, "BOTTOM", 0, -15)
	frame.rollButton = CreateButton(frame, "Roll Dice", 154, 29, "primary")
	frame.rollButton:SetPoint("TOP", frame.tray, "BOTTOM", 0, -41)
	frame.rollButton:SetScript("OnClick", function()
		local rolled, errorMessage = GuildPing:RollFiveDice()
		if not rolled then GuildPing:Print(errorMessage) end
	end)

	frame.upperLabel = CreateLabel(frame, "NUMBER SCORES", "GameFontNormalSmall")
	frame.upperLabel:SetPoint("TOPLEFT", 24, -334)
	frame.upperLabel:SetTextColor(Unpack(DICE_AMBER))
	frame.combinationLabel = CreateLabel(frame, "COMBINATION SCORES", "GameFontNormalSmall")
	frame.combinationLabel:SetPoint("TOPLEFT", 444, -334)
	frame.combinationLabel:SetTextColor(Unpack(DICE_AMBER))
	frame.scoreRows = {}
	for index, category in ipairs(self:GetFiveDiceCategories()) do
		local columnIndex = index <= 6 and index or index - 6
		local row = CreateScoreRow(frame, category, 392)
		row:SetPoint("TOPLEFT", index <= 6 and 24 or 444, -355 - ((columnIndex - 1) * 36))
		frame.scoreRows[index] = row
	end

	frame.totalBar = CreateFrame("Frame", nil, frame, "BackdropTemplate")
	frame.totalBar:SetPoint("TOPLEFT", 24, -616)
	frame.totalBar:SetPoint("TOPRIGHT", -24, -616)
	frame.totalBar:SetHeight(54)
	frame.totalBar:SetBackdrop(ROW_BACKDROP)
	frame.totalBar:SetBackdropColor(0.10, 0.075, 0.045, 1)
	frame.totalBar:SetBackdropBorderColor(Unpack(DICE_AMBER))
	frame.upperTotalText = CreateLabel(frame.totalBar, "", "GameFontHighlight")
	frame.upperTotalText:SetPoint("LEFT", 16, 0)
	frame.bonusText = CreateLabel(frame.totalBar, "", "GameFontHighlight")
	frame.bonusText:SetPoint("CENTER", 0, 0)
	frame.grandTotalText = CreateLabel(frame.totalBar, "", "GameFontNormalLarge")
	frame.grandTotalText:SetPoint("RIGHT", -16, 0)
	frame.grandTotalText:SetTextColor(Unpack(DICE_CREAM))

	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 Game", 118, 25, "primary")
	frame.newGameButton:SetPoint("BOTTOM", 0, 16)
	frame.newGameButton:SetScript("OnClick", function()
		local game = GuildPing.activeFiveDiceGame
		if game and game.status == "ACTIVE" and (game.round > 1 or game.rollCount > 0) then StaticPopup_Show(RESTART_POPUP_KEY)
		else GuildPing:StartFiveDiceGame() 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:RefreshFiveDiceFrame() end)
	frame:SetScript("OnHide", function()
		frame.rollAnimation = nil
		frame:SetScript("OnUpdate", nil)
		for _, die in ipairs(frame.dice) do die:SetScale(1) end
	end)
	frame:Hide()
	self.fiveDiceFrame = frame
	return frame
end

function GuildPing:AnimateFiveDiceRoll(game, rolledIndices)
	local frame = self.fiveDiceFrame
	if not frame or not frame:IsShown() or game ~= self.activeFiveDiceGame then return end
	local rolling = {}
	for _, index in ipairs(rolledIndices or {}) do rolling[index] = true end
	frame.rollAnimation = { gameID = game.id, elapsed = 0, duration = 0.85, rolling = rolling }
	frame:SetScript("OnUpdate", function(_, elapsed) GuildPing:UpdateFiveDiceAnimation(elapsed) end)
end

function GuildPing:UpdateFiveDiceAnimation(elapsed)
	local frame, game = self.fiveDiceFrame, self.activeFiveDiceGame
	local animation = frame and frame.rollAnimation
	if not frame or not animation then return end
	if not game or game.id ~= animation.gameID then
		frame.rollAnimation = nil frame:SetScript("OnUpdate", nil) return
	end
	animation.elapsed = animation.elapsed + elapsed
	for index, die in ipairs(frame.dice) do
		if animation.rolling[index] then
			local beat = math.floor(animation.elapsed * 22)
			die.valueText:SetText(tostring(((beat + (index * 3)) % 6) + 1))
			die:SetScale(0.94 + (math.abs(math.sin((animation.elapsed * 18) + index)) * 0.12))
		end
	end
	if animation.elapsed >= animation.duration then
		frame.rollAnimation = nil
		frame:SetScript("OnUpdate", nil)
		for _, die in ipairs(frame.dice) do die:SetScale(1) end
		self:RefreshFiveDiceFrame()
	end
end

function GuildPing:RefreshFiveDiceFrame()
	local frame, game = self.fiveDiceFrame, self.activeFiveDiceGame
	if not frame then return end
	if not game then frame:Hide() return end
	local animation = frame.rollAnimation
	frame.roundText:SetText(game.status == "FINISHED" and "Scorecard complete" or string.format("Round %d of %d", game.round, self.FIVE_DICE_CATEGORY_COUNT))
	if animation then
		frame.statusText:SetText("The dice are tumbling…")
		frame.statusText:SetTextColor(WARNING_GOLD:GetRGB())
	elseif game.status == "FINISHED" then
		frame.statusText:SetText(string.format("Final score: %d", game.totalScore or 0))
		frame.statusText:SetTextColor(GUILD_GREEN:GetRGB())
	elseif game.rollCount == 0 then
		frame.statusText:SetText("Roll all five dice to begin this round.")
		frame.statusText:SetTextColor(MUTED_COLOR:GetRGB())
	elseif game.rollCount < self.FIVE_DICE_MAX_ROLLS then
		frame.statusText:SetText(string.format("%d roll%s left • click dice to hold them, or choose a score", self.FIVE_DICE_MAX_ROLLS - game.rollCount,
			self.FIVE_DICE_MAX_ROLLS - game.rollCount == 1 and "" or "s"))
		frame.statusText:SetTextColor(GUILD_GREEN:GetRGB())
	else
		frame.statusText:SetText("No rolls left • choose a score category.")
		frame.statusText:SetTextColor(WARNING_GOLD:GetRGB())
	end

	for index, die in ipairs(frame.dice) do
		local isRolling = animation and animation.rolling[index]
		if not isRolling then die.valueText:SetText(game.dice[index] > 0 and tostring(game.dice[index]) or "–") end
		die.holdText:SetText(game.held[index] and "HELD" or "")
		die:SetBackdropColor(Unpack(game.held[index] and { 0.07, 0.18, 0.10, 1 } or { 0.18, 0.11, 0.055, 1 }))
		die:SetBackdropBorderColor(Unpack(game.held[index] and { 0.20, 0.82, 0.40, 1 } or DICE_AMBER))
		die:SetEnabled(game.status == "ACTIVE" and game.rollCount > 0 and not animation)
	end
	local hasFreeDie = false
	for index = 1, 5 do if not game.held[index] then hasFreeDie = true break end end
	frame.rollButton:SetShown(game.status == "ACTIVE")
	frame.rollButton:SetEnabled(not animation and hasFreeDie and game.rollCount < self.FIVE_DICE_MAX_ROLLS)
	frame.rollButton:SetText(game.rollCount == 0 and "Roll Dice" or "Roll Again")

	for index, category in ipairs(self:GetFiveDiceCategories()) do
		local row = frame.scoreRows[index]
		local recorded = game.scores[category.key]
		row.used = recorded ~= nil
		row.disabledByAnimation = animation ~= nil
		row:SetBackdropColor(Unpack(row.used and ROW_USED or ROW_DARK))
		row:SetBackdropBorderColor(Unpack(row.used and { 0.16, 0.18, 0.23, 1 } or { 0.20, 0.23, 0.31, 1 }))
		row.nameText:SetTextColor((row.used and MUTED_COLOR or UI.TextColor):GetRGB())
		if recorded ~= nil then
			row.scoreText:SetText(tostring(recorded))
			row.scoreText:SetTextColor(MUTED_COLOR:GetRGB())
		elseif game.rollCount > 0 then
			row.scoreText:SetText("score " .. tostring(self:GetFiveDiceCategoryScore(category.key, game.dice)))
			row.scoreText:SetTextColor(GUILD_GREEN:GetRGB())
		else
			row.scoreText:SetText("—")
			row.scoreText:SetTextColor(MUTED_COLOR:GetRGB())
		end
		row:SetEnabled(game.status == "ACTIVE" and game.rollCount > 0 and not animation and not row.used)
	end

	local upper, bonus, _, grandTotal = self:GetFiveDiceTotals(game)
	frame.upperTotalText:SetText(string.format("Upper  %d / 63", upper))
	frame.bonusText:SetText(bonus > 0 and "Upper bonus  +35" or string.format("Bonus needs %d", math.max(0, 63 - upper)))
	frame.bonusText:SetTextColor((bonus > 0 and GUILD_GREEN or MUTED_COLOR):GetRGB())
	frame.grandTotalText:SetText("TOTAL  " .. grandTotal)
	frame.newGameButton:SetText(game.status == "FINISHED" and "Play Again" or "New Game")
end

function GuildPing:ShowFiveDice()
	if not self.activeFiveDiceGame then self:StartFiveDiceGame() 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.runeMastermindFrame then self.runeMastermindFrame:Hide() end
	if self.spectatorLobby then self.spectatorLobby:Hide() end
	if self.spectatorGameWindow then self.spectatorGameWindow:Hide() end
	local frame = self:CreateFiveDiceFrame()
	frame:Show()
	self:RefreshFiveDiceFrame()
end
