--[[
    Cerebro - UISandbox Standings Tab
    Extracted from UI/UISandbox.lua (Phase 2a decomposition)

    @module UI.Tabs.Standings
    @version 0.12.0
]]--

local AddonName = "Cerebro"
local Cerebro = LibStub("AceAddon-3.0"):GetAddon(AddonName)
local AceGUI = LibStub("AceGUI-3.0")
local UISandbox = Cerebro:GetModule("UISandbox")

--[[=============================================
	Tab: Standings (Player Rankings)
===============================================]]

-- Standings state (persistent across tab switches)
UISandbox.standingsSortKey = "pr"
UISandbox.standingsSortAsc = false
UISandbox.standingsFilter = ""

-- Column Schema (matching StandingsFrame.lua exactly)
local STANDINGS_COLUMNS = {
	{ id = "rank", label = "#", width = 25, sortKey = "rank", numeric = true },
	{ id = "name", label = "Player", width = 110, sortKey = "name", numeric = false },
	{ id = "guildRank", label = "Guild Rank", width = 70, sortKey = "guildRank", numeric = false },
	{ id = "pr", label = "PR", width = 50, sortKey = "pr", numeric = true },
	{ id = "ep", label = "EP", width = 40, sortKey = "ep", numeric = true },
	{ id = "gp", label = "GP", width = 40, sortKey = "gp", numeric = true },
	{ id = "status", label = "Status", width = 50, sortKey = "status", numeric = false },
	{ id = "attendance", label = "Attend", width = 55, sortKey = "attendancePct", numeric = true },
}

-- Attendance calculation (ported from StandingsFrame.lua)
function UISandbox:CalculateAttendanceDisplay(playerData)
	local bossKillsPresent = playerData.bossKillsPresent or 0
	local coreAdmittedAt = playerData.coreAdmittedAt

	if not coreAdmittedAt then
		return "- (NEW)", {r=0.7, g=0.7, b=0.7}
	end

	-- Use eligibleBossKills (pre-computed by officer, synced to all clients)
	local eligibleBosses = playerData.eligibleBossKills or 0

	if eligibleBosses == 0 then
		return "- (NEW)", {r=0.7, g=0.7, b=0.7}
	end

	-- Cap at 100% for display sanity
	local pct = math.min(100, math.floor((bossKillsPresent / eligibleBosses) * 100))
	local displayText = string.format("%d%%", pct)

	local color
	if pct >= 95 then
		color = {r=0.0, g=1.0, b=0.0}
	elseif pct >= 80 then
		color = {r=1.0, g=1.0, b=0.0}
	else
		color = {r=1.0, g=0.0, b=0.0}
	end

	return displayText, color, pct
end

function UISandbox:DrawStandingsTab(container)
	local isOfficer = Cerebro:IsOfficer()

	-- Check if current player is Pending (non-officers can't see standings if pending)
	local myName = Cerebro:GetFullPlayerName(UnitName("player"))
	local myData = Cerebro:GetPlayerData(myName) or {}

	if myData.status == "Pending" and not isOfficer then
		local awaitLabel = AceGUI:Create("Label")
		awaitLabel:SetText("\n\n|cffff0000Awaiting Officer Approval...|r\n\nYou are currently in 'Pending' status.\nYour EP/GP data is visible in 'My Progress',\nbut you will not appear in the Standings until an officer\nadmits you or starts a raid session.")
		awaitLabel:SetFullWidth(true)
		container:AddChild(awaitLabel)
		return
	end

	-- Store reference for refresh
	self.standingsContainer = container

	-- ============================================
	-- FILTER/SEARCH BAR (Using AceGUI for proper focus)
	-- ============================================
	local filterGroup = AceGUI:Create("SimpleGroup")
	filterGroup:SetFullWidth(true)
	filterGroup:SetLayout("Flow")
	container:AddChild(filterGroup)

	local searchBox = AceGUI:Create("EditBox")
	searchBox:SetLabel("")
	searchBox:SetWidth(150)
	searchBox:SetText(self.standingsFilter or "")
	searchBox:DisableButton(true)
	searchBox:SetCallback("OnTextChanged", function(widget, event, text)
		self.standingsFilter = (text or ""):lower()
		-- Just refresh the rows, don't rebuild entire tab
		self:RefreshStandingsRows()
	end)
	filterGroup:AddChild(searchBox)

	local searchHint = AceGUI:Create("Label")
	searchHint:SetText("  |cff888888Search...|r")
	searchHint:SetWidth(80)
	filterGroup:AddChild(searchHint)

	-- Spacer to push sync button right
	local spacer = AceGUI:Create("Label")
	spacer:SetText("")
	spacer:SetWidth(80)
	filterGroup:AddChild(spacer)

	-- Sync Button (Officer: also broadcasts discovery to guild addon users)
	local syncBtn = AceGUI:Create("Button")
	syncBtn:SetText(isOfficer and "Sync" or "Request")
	syncBtn:SetWidth(80)
	syncBtn:SetCallback("OnClick", function()
		if isOfficer then
			-- Existing sync functionality
			Cerebro:RepairDatabase()
			if Cerebro.modules.CommunicationV2 then
				local sent = Cerebro.modules.CommunicationV2:SyncToRaid()

				-- Enhanced: Scan guild for addon users (BroadcastDiscovery)
				-- Only guild members WITH the addon will respond and be added as Pending
				Cerebro.modules.CommunicationV2:BroadcastDiscovery()

				if sent then
					Cerebro:Print("Data synced. Scanning guild for addon users...")
				else
					Cerebro:Print("|cffff8800Sync deferred until combat ends.|r Scanning guild for addon users...")
				end
			end
		else
			if Cerebro.modules.CommunicationV2 then
				Cerebro.modules.CommunicationV2:RequestSync()
			end
			Cerebro:Print("Sync requested from officers...")
		end
		self:RefreshStandingsRows()
	end)
	filterGroup:AddChild(syncBtn)

	-- Small spacer
	local spacer2 = AceGUI:Create("Label")
	spacer2:SetText(" ")
	spacer2:SetFullWidth(true)
	container:AddChild(spacer2)

	-- ============================================
	-- HEADER ROW (Clickable for sorting)
	-- ============================================
	local headerGroup = AceGUI:Create("SimpleGroup")
	headerGroup:SetFullWidth(true)
	headerGroup:SetLayout("Flow")
	container:AddChild(headerGroup)

	-- Store reference for header refresh
	self.standingsHeaderGroup = headerGroup

	self:RenderStandingsHeaders()

	-- ============================================
	-- SCROLLABLE DATA AREA
	-- ============================================
	local scroll = AceGUI:Create("ScrollFrame")
	scroll:SetLayout("Flow")
	scroll:SetFullWidth(true)
	scroll:SetFullHeight(true)
	container:AddChild(scroll)

	self.standingsScrollFrame = scroll
	self.standingsIsOfficer = isOfficer

	-- Initial render
	self:RefreshStandingsRows()
end

function UISandbox:RenderStandingsHeaders()
	local headerGroup = self.standingsHeaderGroup
	if not headerGroup then return end

	headerGroup:ReleaseChildren()

	for _, col in ipairs(STANDINGS_COLUMNS) do
		local btn = AceGUI:Create("InteractiveLabel")

		-- Build label: gold column name + gray sort indicator (if this column is sorted)
		local labelText = "|cffFFD700" .. col.label .. "|r"
		if self.standingsSortKey == col.sortKey then
			local arrow = self.standingsSortAsc and " ^" or " v"
			labelText = labelText .. "|cff666666" .. arrow .. "|r"
		end

		btn:SetText(labelText)
		btn:SetWidth(col.width)
		btn:SetCallback("OnClick", function()
			if self.standingsSortKey == col.sortKey then
				self.standingsSortAsc = not self.standingsSortAsc
			else
				self.standingsSortKey = col.sortKey
				-- Numeric columns default to descending, string columns to ascending
				self.standingsSortAsc = (col.numeric == false)
			end
			-- Refresh both headers (for sort indicator) and rows (for data)
			self:RenderStandingsHeaders()
			self:RefreshStandingsRows()
		end)
		headerGroup:AddChild(btn)
	end
end

function UISandbox:RefreshStandingsRows()
	local scroll = self.standingsScrollFrame
	if not scroll then return end

	scroll:ReleaseChildren()

	local isOfficer = self.standingsIsOfficer

	-- Get and filter players
	local players = Cerebro:GetAllPlayers() or {}
	local filtered = {}

	for _, p in ipairs(players) do
		local name = p.name or ""
		local status = p.status or "Core"

		-- RBAC: Exclude Pending players for non-officers
		if not (status == "Pending" and not isOfficer) then
			-- Apply filter text
			if self.standingsFilter == "" or name:lower():find(self.standingsFilter, 1, true) then
				-- Compute attendance percentage for sorting
				local _, _, pct = self:CalculateAttendanceDisplay(p)
				p.attendancePct = pct or 0
				table.insert(filtered, p)
			end
		end
	end

	-- Sort players
	local columns = STANDINGS_COLUMNS
	table.sort(filtered, function(a, b)
		local valA = a[self.standingsSortKey]
		local valB = b[self.standingsSortKey]

		if valA == nil then valA = 0 end
		if valB == nil then valB = 0 end

		local isNumeric = false
		for _, col in ipairs(columns) do
			if col.sortKey == self.standingsSortKey then
				isNumeric = col.numeric
				break
			end
		end

		if not isNumeric then
			valA = tostring(valA):lower()
			valB = tostring(valB):lower()
		end

		if valA ~= valB then
			if self.standingsSortAsc then
				return valA < valB
			else
				return valA > valB
			end
		end

		local nameA = (a.name or ""):lower()
		local nameB = (b.name or ""):lower()
		return nameA < nameB
	end)

	-- Render rows
	if #filtered == 0 then
		local noResults = AceGUI:Create("Label")
		noResults:SetText("|cff888888No players found matching filter.|r")
		noResults:SetFullWidth(true)
		scroll:AddChild(noResults)
	else
		for i, data in ipairs(filtered) do
			self:CreateStandingsRowAceGUI(scroll, data, i, (i % 2 == 0))
		end
	end
end

function UISandbox:CreateStandingsRowAceGUI(container, data, rank, isStripe)
	-- ============================================
	-- DATA EXTRACTION (full Name-Realm always)
	-- ============================================
	local name = data.name or "Unknown"  -- FULL Name-Realm, never strip!
	local class = data.class or "Unknown"
	local pr = data.pr or 0
	local ep = data.ep or 0
	local gp = data.gp or 0
	local status = data.status or "Core"

	-- Guild rank
	local guildRank = "Unknown"
	local success, rank_fetch = pcall(function()
		return Cerebro:GetPlayerGuildRank(name)
	end)
	if success and rank_fetch then
		guildRank = rank_fetch
	end

	-- Attendance
	local attendText, attendColor = self:CalculateAttendanceDisplay(data)

	-- Row group
	local rowGroup = AceGUI:Create("SimpleGroup")
	rowGroup:SetFullWidth(true)
	rowGroup:SetLayout("Flow")
	container:AddChild(rowGroup)

	-- ============================================
	-- RIGHT-CLICK HANDLER (Officer Context Menu)
	-- ============================================
	if Cerebro:IsOfficer() and rowGroup.frame then
		rowGroup.frame:EnableMouse(true)
		rowGroup.frame:SetScript("OnMouseUp", function(self, button)
			if button == "RightButton" then
				UISandbox:ShowStandingsContextMenu(self, name, data)
			end
		end)
	end

	-- ============================================
	-- COLUMNS (matching STANDINGS_COLUMNS order)
	-- ============================================

	-- Rank
	local rankLabel = AceGUI:Create("Label")
	rankLabel:SetText("|cff888888" .. rank .. "|r")
	rankLabel:SetWidth(25)
	rowGroup:AddChild(rankLabel)

	-- Player Name (FULL Name-Realm with class color)
	-- NOTE: Use GetClassColor directly, NOT GetClassColorString
	-- GetClassColorString incorrectly calls GetPlayerData(name), which would
	-- auto-create entries if passed a class name like "WARRIOR"
	local nameLabel = AceGUI:Create("Label")
	local cc = Cerebro:GetClassColor(class)
	local colorStr = string.format("|cff%02x%02x%02x", cc.r * 255, cc.g * 255, cc.b * 255)
	nameLabel:SetText(colorStr .. name .. "|r")
	nameLabel:SetWidth(110)
	rowGroup:AddChild(nameLabel)

	-- Guild Rank
	local guildLabel = AceGUI:Create("Label")
	guildLabel:SetText("|cffaaaaaa" .. guildRank .. "|r")
	guildLabel:SetWidth(70)
	rowGroup:AddChild(guildLabel)

	-- PR (gold)
	local prLabel = AceGUI:Create("Label")
	prLabel:SetText("|cffFFD700" .. string.format("%.2f", pr) .. "|r")
	prLabel:SetWidth(50)
	rowGroup:AddChild(prLabel)

	-- EP
	local epLabel = AceGUI:Create("Label")
	epLabel:SetText(string.format("%d", ep))
	epLabel:SetWidth(40)
	rowGroup:AddChild(epLabel)

	-- GP
	local gpLabel = AceGUI:Create("Label")
	gpLabel:SetText(string.format("%d", gp == 0 and 1 or gp))
	gpLabel:SetWidth(40)
	rowGroup:AddChild(gpLabel)

	-- Status
	local statusLabel = AceGUI:Create("Label")
	if status == "Pending" then
		statusLabel:SetText("|cffff0000Pending|r")
	else
		statusLabel:SetText("|cff00ff00" .. status .. "|r")
	end
	statusLabel:SetWidth(50)
	rowGroup:AddChild(statusLabel)

	-- Attendance (color-coded)
	local attendLabel = AceGUI:Create("Label")
	local attendColorStr = string.format("|cff%02x%02x%02x",
		math.floor(attendColor.r * 255),
		math.floor(attendColor.g * 255),
		math.floor(attendColor.b * 255))
	attendLabel:SetText(attendColorStr .. attendText .. "|r")
	attendLabel:SetWidth(55)
	rowGroup:AddChild(attendLabel)
end
