--[[
    Cerebro - Command Registry: Admin Commands
    Extracted from UI/CommandRegistry.lua (Phase 2a decomposition)

    Commands:
      admit_player        (original lines 1261-1335)
      fix_database        (original lines 1337-1383)
      purge_pending       (original lines 1385-1438)
      delete_player       (original lines 1440-1536)
      set_epoch           (original lines 1538-1572)
      season_reset        (original lines 1574-1633)
      standings_reset     (original lines 1635-1674)
      factory_reset       (original lines 1676-1828)
      fresh_start         (original lines 1830-1949)
      restore_backup      (original lines 1951-2044)
      backup_status       (original lines 2046-2090)
      quarantine_status   (original lines 2092-2137)
      purge_player        (original lines 2139-2205)
      block_player        (original lines 2206-2281)
      unblock_player      (original lines 2282-2332)
      blocked_list        (original lines 2333-2357)
      purge_test_players  (original lines 2359-2411)
      purge_ghosted       (original lines 2413-2508)
      test_season_reset   (original lines 2650-2690)

    @module UI.Commands.Admin
    @version 0.12.0
]]--

local AddonName, PrivateTable = ...
local Cerebro = LibStub("AceAddon-3.0"):GetAddon(AddonName)

-- Shared helper: normalize a player key for case-insensitive, realm-normalized comparison
-- Used by delete_player, purge_player, and block_player handlers.
-- NOTE: This is a copy of the file-local NormalizePlayerKey in CommandRegistry.lua.
-- Both must be kept in sync until CommandRegistry.lua removes its copy after Phase 2b.
local function NormalizePlayerKey(key)
	local name, realm = strsplit("-", key)
	if realm then
		return (name or ""):lower() .. "-" .. Cerebro:NormalizeRealmString(realm):lower()
	end
	return key:lower()
end

Cerebro._commandDefs = Cerebro._commandDefs or {}
local defs = Cerebro._commandDefs

-- Admit Player to Core
defs[#defs + 1] = {
	id = "admit_player",
	displayName = "Admit Player to Core",
	description = "Promotes a Pending player to Core status",
	officerOnly = true,
	hidden = false,
	dangerous = false,
	aliases = {"admit"},
	slash = "/cb admit [player]",
	usage = "/cb admit [player]",
	argsSchema = {
		{name = "player", type = "string", required = true},
	},
	handler = function(ctx, argv)
		local player = argv[1]

		if not player then
			Cerebro:Print("Usage: /cb admit [player]")
			return
		end

		local fullName = Cerebro:GetFullPlayerName(player)
		local data = Cerebro:GetPlayerData(fullName)

		if data.status == "Core" then
			Cerebro:Print(Cerebro:ColorPlayerName(fullName) .. " is already Core.")
			-- Still refresh UI and repair database in case they're not visible
			Cerebro:RepairDatabase()
			if Cerebro.modules.CommunicationV2 then
				Cerebro.modules.CommunicationV2:SyncToRaid()
			end
			Cerebro:RefreshAllUI()
			return
		end

		-- Try to populate class from guild roster if missing
		local classToSet = data.class
		if not classToSet then
			for i = 1, GetNumGuildMembers() do
				local name, _, _, _, _, _, _, _, _, _, classFileName = GetGuildRosterInfo(i)
				if name and Cerebro:GetFullPlayerName(name) == fullName then
					classToSet = classFileName
					Cerebro:Debug("Fetched class for " .. fullName .. ": " .. classFileName)
					break
				end
			end
		end

		-- Apply via write gate (ADR-001)
		local admitChanges = { status = "Core", coreAdmittedAt = time() }
		if classToSet then admitChanges.class = classToSet end
		Cerebro:ApplyPlayerMutation(fullName, admitChanges)

		Cerebro:Print(string.format("Admitted %s to Core.", Cerebro:ColorPlayerName(fullName)))

		-- Audit log
		if Cerebro.Ledger then
			Cerebro.Ledger:AddAuditLog("ADMIT_PLAYER", "Promoted " .. fullName .. " to Core status.")
		end

		-- Broadcast update (Real-time sync) - include class, coreAdmittedAt, and lastUpdate for LWW
		if Cerebro.modules.CommunicationV2 and Cerebro.modules.CommunicationV2.BroadcastPlayerUpdate then
			Cerebro.modules.CommunicationV2:BroadcastPlayerUpdate(fullName, {
				status = "Core",
				class = data.class,
				coreAdmittedAt = data.coreAdmittedAt,
				lastUpdate = data.lastUpdate
			})
		end

		-- Refresh UI if open
		Cerebro:RefreshAllUI()
	end
}

-- Fix Database (DANGEROUS)
defs[#defs + 1] = {
	id = "fix_database",
	displayName = "Fix Database",
	description = "Removes invalid player entries from the database",
	officerOnly = true,
	hidden = true,
	dangerous = true,
	slash = "/cb fixdb YES",
	usage = "/cb fixdb YES",
	handler = function(ctx, argv)
		-- Dangerous command confirmation
		if ctx.source == "slash" and (not argv[1] or argv[1]:upper() ~= "YES") then
			Cerebro:Print("|cffff0000DANGEROUS:|r This command removes invalid player entries.")
			Cerebro:Print("Re-run with YES to confirm: |cff00ff00/cb fixdb YES|r")
			return
		end

		Cerebro:Print("Running database cleanup...")
		local count = 0
		local toDelete = {}

		for name, data in pairs(Cerebro.db.global.players) do
			local n = name:lower()
			local shortName = Cerebro:GetShortPlayerName(name):lower()

			if n == "player" or shortName == "player" or n == "unknown" or n:match("^unknown") or (not n:find("-")) then
				table.insert(toDelete, name)
			end
		end

		for _, name in ipairs(toDelete) do
			Cerebro.db.global.players[name] = nil
			count = count + 1
			Cerebro:Print("Removed invalid entry: " .. name)
		end

		if count > 0 then
			Cerebro:Print(string.format("Cleanup complete. Removed %d entries. Please /reload.", count))
			if Cerebro.Ledger then
				Cerebro.Ledger:AddAuditLog("DB_FIX", string.format("Cleaned up %d invalid entries: %s", count, table.concat(toDelete, ", ")))
			end
		else
			Cerebro:Print("Database seems clean (no invalid entries found).")
		end
	end
}

-- Purge All Pending Players (DANGEROUS)
defs[#defs + 1] = {
	id = "purge_pending",
	displayName = "Purge Pending Players",
	description = "Removes ALL Pending status players from the database",
	officerOnly = true,
	hidden = false,
	dangerous = true,
	slash = "/cb purgepending YES",
	usage = "/cb purgepending YES",
	argsSchema = {
		{name = "confirm", type = "string", required = true, values = {"YES"}},
	},
	handler = function(ctx, argv)
		-- Dangerous command confirmation
		if ctx.source == "slash" and (not argv[1] or argv[1]:upper() ~= "YES") then
			-- Count pending players first
			local count = 0
			for name, data in pairs(Cerebro.db.global.players) do
				if data.status == "Pending" then
					count = count + 1
				end
			end
			Cerebro:Print(string.format("|cffff0000DANGEROUS:|r This will remove %d Pending players from the database.", count))
			Cerebro:Print("Re-run with YES to confirm: |cff00ff00/cb purgepending YES|r")
			return
		end

		local count = 0
		local toDelete = {}

		for name, data in pairs(Cerebro.db.global.players) do
			if data.status == "Pending" then
				table.insert(toDelete, name)
			end
		end

		for _, name in ipairs(toDelete) do
			Cerebro.db.global.players[name] = nil
			count = count + 1
		end

		if count > 0 then
			Cerebro:Print(string.format("|cff00ff00Purged %d Pending players.|r Database cleaned.", count))
			if Cerebro.Ledger then
				Cerebro.Ledger:AddAuditLog("PURGE_PENDING", string.format("Removed %d Pending players from database.", count))
			end
			-- Refresh UI
			Cerebro:RefreshAllUI()
		else
			Cerebro:Print("No Pending players found in database.")
		end
	end
}

-- Delete Player (DANGEROUS)
defs[#defs + 1] = {
	id = "delete_player",
	displayName = "Delete Player",
	description = "Permanently removes a player from the database",
	officerOnly = true,
	hidden = true,
	dangerous = true,
	slash = "/cb delete [player] YES  (use quotes for realms with spaces)",
	usage = "/cb delete [player] YES",
	argsSchema = {
		{name = "player", type = "string", required = true},
		{name = "confirm", type = "string", required = true, values = {"YES"}},
	},
	handler = function(ctx, argv)
		local player = argv[1]

		if not player then
			Cerebro:Print("Usage: /cb delete [player] YES")
			Cerebro:Print("  Tip: Use quotes for realms with spaces: /cb delete \"Name-Area 52\" YES")
			Cerebro:Print("  Or use normalized name (no spaces): /cb delete Name-Area52 YES")
			return
		end

		-- Dangerous command confirmation
		if ctx.source == "slash" and (not argv[2] or argv[2]:upper() ~= "YES") then
			Cerebro:Print("|cffff0000DANGEROUS:|r This will permanently delete " .. player)
			Cerebro:Print("Re-run with YES to confirm: |cff00ff00/cb delete " .. player .. " YES|r")
			return
		end

		local inputNormalized = NormalizePlayerKey(player)

		-- Search case-insensitive (Pass 1: exact match)
		local targetKey = nil
		for k, v in pairs(Cerebro.db.global.players) do
			if k:lower() == player:lower() then
				targetKey = k
				break
			end
		end

		-- Pass 2: Try appending current realm
		if not targetKey then
			local withRealm = player .. "-" .. GetRealmName():gsub("%s", "")
			for k, v in pairs(Cerebro.db.global.players) do
				if k:lower() == withRealm:lower() then
					targetKey = k
					break
				end
			end
		end

		-- Pass 3: Normalized realm matching (handles "Area52" matching "Area 52")
		if not targetKey then
			for k, v in pairs(Cerebro.db.global.players) do
				if NormalizePlayerKey(k) == inputNormalized then
					targetKey = k
					break
				end
			end
		end

		if targetKey then
			-- Instead of deleting, set status to Pending so they can re-announce (via write gate — ADR-001)
			local resetGP = Cerebro.db.global.config.epgp.baseGP or 200
			Cerebro:ApplyPlayerMutation(targetKey, {
				status = "Pending",
				ep = 0,
				gp = resetGP,
				pr = 0,
			})
			local data = Cerebro.db.global.players[targetKey]

			Cerebro:Print("Reset " .. targetKey .. " to Pending status (can be re-admitted).")

			-- Audit log
			if Cerebro.Ledger then
				Cerebro.Ledger:AddAuditLog("PLAYER_RESET", "Reset to Pending: " .. targetKey)
			end

			-- Broadcast update and refresh
			if Cerebro.modules.CommunicationV2 and Cerebro.modules.CommunicationV2.BroadcastPlayerUpdate then
				Cerebro.modules.CommunicationV2:BroadcastPlayerUpdate(targetKey, {
					status = "Pending",
					ep = 0,
					gp = data.gp,
					pr = 0,
					lastUpdate = data.lastUpdate
				})
			end
			Cerebro:RefreshAllUI()
		else
			Cerebro:Print("Player not found in database: " .. player)
		end
	end
}

-- Manual Epoch Set (Dangerous - Advanced)
defs[#defs + 1] = {
	id = "set_epoch",
	displayName = "Set Epoch",
	description = "Manually sets the sync epoch to a specific value. DANGEROUS.",
	officerOnly = true,
	hidden = true,
	dangerous = true,
	slash = "/cb setepoch [value] YES",
	usage = "/cb setepoch [value] YES",
	argsSchema = {
		{name = "value", type = "number", required = true},
		{name = "confirm", type = "string", required = true, values = {"YES"}},
	},
	handler = function(ctx, argv)
		local val = tonumber(argv[1])

		if ctx.source == "slash" and (not val or not argv[2] or argv[2]:upper() ~= "YES") then
			Cerebro:Print("|cffff0000DANGEROUS:|r Manually setting epoch can invalidate valid sync packets.")
			Cerebro:Print("Current Epoch: " .. (Cerebro.db.global.resetEpoch or 0))
			Cerebro:Print("Usage: |cff00ff00/cb setepoch [new_value] YES|r")
			return
		end

		local oldEpoch = Cerebro.db.global.resetEpoch or 0
		Cerebro.db.global.resetEpoch = val

		Cerebro:Print(string.format("|cff00ff00Epoch Updated:|r %d -> %d", oldEpoch, val))
		Cerebro:Print("Run /cb sync to broadcast this new epoch to the raid.")

		if Cerebro.Ledger then
			Cerebro.Ledger:AddAuditLog("MANUAL_EPOCH_SET", string.format("Changed from %d to %d by %s", oldEpoch, val, ctx.sender))
		end
	end
}

-- Season Reset (Manual)
defs[#defs + 1] = {
	id = "season_reset",
	displayName = "Season Reset",
	description = "Resets EP/GP/M+ for a new season (preserves history)",
	officerOnly = true,
	hidden = false,
	dangerous = true,
	aliases = {"seasonreset"},
	slash = "/cb seasonreset YES",
	usage = "/cb seasonreset YES",
	argsSchema = {
		{name = "confirm", type = "string", required = true, values = {"YES"}},
	},
	handler = function(ctx, argv)
		-- Dangerous command confirmation
		if ctx.source == "slash" and (not argv[1] or argv[1]:upper() ~= "YES") then
			Cerebro:Print("|cffff8800SEASON RESET:|r This will wipe EP/GP and M+ history for everyone.")
			Cerebro:Print("  • All player EP → 0")
			Cerebro:Print("  • All player GP → Base GP")
			Cerebro:Print("  • All M+ runs cleared (personal + guild)")
			Cerebro:Print("  • Session history + attendance cleared")
			Cerebro:Print("  • Epoch incremented (forces sync)")
			Cerebro:Print("Re-run with YES to confirm: |cff00ff00/cb seasonreset YES|r")
			return
		end

		-- Shared core: zero EP/GP, clear attendance/history, increment epoch, sync
		local playerCount = Cerebro:ResetStandings("SEASON_RESET")

		-- Season-specific: Clear M+ history
		local mplusCount = Cerebro.db.global.mplusRuns and #Cerebro.db.global.mplusRuns or 0
		Cerebro.db.global.mplusRuns = {}
		local guildMplusCount = Cerebro.db.global.guildMplusRuns and #Cerebro.db.global.guildMplusRuns or 0
		Cerebro.db.global.guildMplusRuns = {}

		-- Season-specific: Clear readiness tracking
		if Cerebro.db.global.readiness then
			Cerebro.db.global.readiness.players = {}
			Cerebro.db.global.readiness.week = nil
		end

		-- Season-specific: Update season ID and TOC tracking
		local seasonInfo = Cerebro:GetSeasonInfo()
		Cerebro.db.global.seasonID = seasonInfo and seasonInfo.seasonID or 0
		local _, _, _, toc = GetBuildInfo()
		if toc then
			Cerebro.db.global.lastKnownTOC = toc
		end

		-- Print summary
		Cerebro:Print("|cff00ff00[Season Reset Complete]|r")
		Cerebro:Print(string.format("  - Reset %d players", playerCount))
		Cerebro:Print(string.format("  - Cleared %d personal + %d guild M+ runs", mplusCount, guildMplusCount))
		Cerebro:Print(string.format("  - Epoch = %d", Cerebro.db.global.resetEpoch))

		-- Refresh UI if open
		Cerebro:RefreshAllUI()
	end
}

-- Standings Reset (Zeroes EP/GP, preserves M+ and readiness)
defs[#defs + 1] = {
	id = "standings_reset",
	displayName = "Standings Reset",
	description = "Zeroes EP/GP and attendance for all players. Preserves M+ and readiness data.",
	officerOnly = true,
	hidden = false,
	dangerous = true,
	aliases = {"standingsreset"},
	slash = "/cb standingsreset YES",
	usage = "/cb standingsreset YES",
	argsSchema = {
		{name = "confirm", type = "string", required = true, values = {"YES"}},
	},
	handler = function(ctx, argv)
		-- Dangerous command confirmation
		if ctx.source == "slash" and (not argv[1] or argv[1]:upper() ~= "YES") then
			Cerebro:Print("|cffff8800STANDINGS RESET:|r This will zero all EP/GP and attendance for everyone.")
			Cerebro:Print("  - All player EP -> 0")
			Cerebro:Print("  - All player GP -> Base GP")
			Cerebro:Print("  - All attendance -> 0")
			Cerebro:Print("  - Epoch incremented (forces sync)")
			Cerebro:Print("  - M+ and readiness data preserved")
			Cerebro:Print("Re-run with YES to confirm: |cff00ff00/cb standingsreset YES|r")
			return
		end

		-- Shared core: zero EP/GP, clear attendance/history, increment epoch, sync
		local playerCount = Cerebro:ResetStandings("STANDINGS_RESET")

		-- Print summary
		Cerebro:Print("|cff00ff00[Standings Reset Complete]|r")
		Cerebro:Print(string.format("  - Reset %d players", playerCount))
		Cerebro:Print(string.format("  - Epoch = %d", Cerebro.db.global.resetEpoch))
		Cerebro:Print("  - M+ and readiness data preserved")

		-- Refresh UI if open
		Cerebro:RefreshAllUI()
	end
}

-- Factory Reset (EXTREMELY DANGEROUS - GM Only by default)
defs[#defs + 1] = {
	id = "factory_reset",
	displayName = "Factory Reset",
	description = "Wipes all EPGP data (creates backup first). GM-only by default.",
	officerOnly = true, -- Base RBAC check; handler does additional GM check
	hidden = true,
	dangerous = true,
	slash = "/cb factoryreset YES [--force]",
	usage = "/cb factoryreset YES [--force]",
	argsSchema = {
		{name = "confirm", type = "string", required = true, values = {"YES"}},
		{name = "force", type = "string", required = false},
	},
	handler = function(ctx, argv)
		-- TODO: Restore to 72 before v1.0.0 release
		local COOLDOWN_HOURS = 0  -- Temporarily disabled for testing
		local COOLDOWN_SECONDS = COOLDOWN_HOURS * 60 * 60

		-- Check for --force flag
		local forceOverride = false
		for _, arg in ipairs(argv) do
			if arg == "--force" then
				forceOverride = true
				break
			end
		end

		-- Permission Logic:
		-- 1. GM can always reset (with --force to bypass 72h cooldown)
		-- 2. Officers can reset ONLY if allowOfficerFactoryReset=true AND no fresh backup
		local isGM = Cerebro:IsGuildMasterSafe()
		local allowOfficerReset = Cerebro.db.global.config.admin and Cerebro.db.global.config.admin.allowOfficerFactoryReset
		local backup = Cerebro.db.global.backup
		local backupAge = backup and (time() - backup.timestamp) or nil
		local hasFreshBackup = backupAge and (backupAge < COOLDOWN_SECONDS)

		-- GM check first (strictest)
		if not isGM then
			-- Not GM - check if officer reset is allowed
			if not allowOfficerReset then
				Cerebro:Print("|cffff0000ACCESS DENIED:|r Factory reset is restricted to Guild Master only.")
				Cerebro:Print("Contact your GM or enable 'Allow Officer Factory Reset' in Admin settings.")
				return
			end

			-- Officer trying to reset with fresh backup - BLOCKED
			if hasFreshBackup then
				local ageHours = math.floor(backupAge / 3600)
				Cerebro:Print("|cffff0000ACCESS DENIED:|r Recent backup exists (" .. ageHours .. "h old, <72h).")
				Cerebro:Print("Officers cannot factory reset while a recent backup is protected.")
				Cerebro:Print("Wait " .. (COOLDOWN_HOURS - ageHours) .. " more hours or contact the Guild Master.")
				return
			end
		else
			-- GM with fresh backup and no --force
			if hasFreshBackup and not forceOverride then
				local ageHours = math.floor(backupAge / 3600)
				Cerebro:Print("|cffff8800WARNING:|r A backup from " .. ageHours .. " hours ago exists.")
				Cerebro:Print("Use |cff00ff00/cb factoryreset YES --force|r to override the 72h protection.")
				return
			end
		end

		-- Confirmation check
		if ctx.source == "slash" and (not argv[1] or argv[1]:upper() ~= "YES") then
			Cerebro:Print("|cffff0000EXTREMELY DANGEROUS:|r This will wipe ALL EPGP data.")
			Cerebro:Print("Re-run with YES to confirm: |cff00ff00/cb factoryreset YES|r")
			return
		end

		-- == BACKUP PHASE ==
		local previousBackupAge = backupAge and math.floor(backupAge / 3600) or nil

		Cerebro.db.global.backup = {
			timestamp = time(),
			version = Cerebro.version,
			players = Cerebro:CopyTable(Cerebro.db.global.players),
			transactions = Cerebro:CopyTable(Cerebro.db.global.transactions),
			audit = Cerebro:CopyTable(Cerebro.db.global.audit),
			epgpConfig = Cerebro:CopyTable(Cerebro.db.global.config.epgp),
			resetEpoch = Cerebro.db.global.resetEpoch or 0, -- Store current epoch for restore
		}

		-- Audit: Backup created (before wipe clears audit)
		if Cerebro.Ledger then
			local backupDetails = "Backup created before factory reset"
			if previousBackupAge then
				backupDetails = backupDetails .. " (previous backup was " .. previousBackupAge .. "h old)"
			end
			Cerebro.Ledger:AddAuditLog("BACKUP_CREATED", backupDetails)
		end

		Cerebro:Print("|cff00ff00Backup created successfully.|r")

		-- == QUARANTINE PHASE ==
		-- Increment epoch to invalidate all older sync payloads
		Cerebro.db.global.resetEpoch = (Cerebro.db.global.resetEpoch or 0) + 1
		-- Set quarantine to block incoming/outgoing sync for 15 min
		Cerebro.db.global.resetQuarantine = { at = time() }

		if Cerebro.Ledger then
			Cerebro.Ledger:AddAuditLog("QUARANTINE_START",
				"Epoch: " .. Cerebro.db.global.resetEpoch .. ", 15 min quarantine active")
		end

		-- == WIPE PHASE ==
		Cerebro.db.global.players = {}
		Cerebro.db.global.transactions = {}
		Cerebro.db.global.awards = {}
		Cerebro.db.global.guildMplusRuns = {}
		Cerebro.db.global.history = {}
		Cerebro.db.global.totalBossKills = 0
		Cerebro.db.global.seasonBossKills = 0
		if Cerebro.db.global.readiness then
			Cerebro.db.global.readiness.players = {}
			Cerebro.db.global.readiness.week = nil
		end
		-- NOTE: Audit is intentionally NOT wiped - preserved for accountability
		-- Old entries will show [Epoch N] markers in UI
		Cerebro.db.global.currentSession = nil

		-- Reset delta sync state
		if Cerebro.db.global.syncState then
			Cerebro.db.global.syncState.dirtyPlayers = {}
			Cerebro.db.global.syncState.nextRev = 0
		end

		-- Reset onboarding flag so wizard appears on fresh install
		if Cerebro.db.global.config then
			Cerebro.db.global.config.onboardingCompleted = false
		end

		-- Add Audit Log (this entry will have the new epoch)
		if Cerebro.Ledger then
			local resetDetails = "Database wiped by " .. UnitName("player")
			if forceOverride then
				resetDetails = resetDetails .. " (--force override used)"
			end
			Cerebro.Ledger:AddAuditLog("FACTORY_RESET", resetDetails)
		end

		-- Broadcast
		if Cerebro.modules.CommunicationV2 then
			Cerebro.modules.CommunicationV2:BroadcastReset()
		end

		-- Refresh UI
		Cerebro:RefreshAllUI()

		Cerebro:Print("|cffff0000Factory Reset Complete.|r Database cleared. Use /cb restore YES to recover.")
	end
}

-- Fresh Start (Local client recovery - available to ALL players)
defs[#defs + 1] = {
	id = "fresh_start",
	displayName = "Fresh Start",
	description = "Resets YOUR local addon data and re-syncs from officers. Does not affect guild epoch or other players.",
	officerOnly = false, -- Available to everyone
	hidden = false,
	dangerous = true,
	slash = "/cb freshstart YES",
	usage = "/cb freshstart YES",
	argsSchema = {
		{name = "confirm", type = "string", required = true, values = {"YES"}},
	},
	handler = function(ctx, argv)
		-- Cooldown tracking (module-level)
		Cerebro._freshStartCooldown = Cerebro._freshStartCooldown or 0
		local COOLDOWN_SECONDS = 60

		-- Cooldown check
		local now = time()
		if (now - Cerebro._freshStartCooldown) < COOLDOWN_SECONDS then
			local remaining = COOLDOWN_SECONDS - (now - Cerebro._freshStartCooldown)
			Cerebro:Print(string.format("|cffff8800Please wait %d seconds|r before running freshstart again.", remaining))
			return
		end

		-- Confirmation check
		if ctx.source == "slash" and (not argv[1] or argv[1]:upper() ~= "YES") then
			Cerebro:Print("|cffff8800Fresh Start|r will wipe YOUR local data and re-sync from officers.")
			Cerebro:Print("Your EP/GP standings, raid attendance, and status will be restored via sync.")
			Cerebro:Print("Only local transaction history and UI preferences are lost.")
			Cerebro:Print("")
			Cerebro:Print("Run |cff00ff00/cb freshstart YES|r to confirm.")
			return
		end

		-- Officer warning: if caller is the only officer online, warn them
		if Cerebro:IsOfficer() then
			-- Count online officers in guild
			local officerCount = 0
			if IsInGuild() then
				for i = 1, GetNumGuildMembers() do
					local name, _, _, _, _, _, _, _, online = GetGuildRosterInfo(i)
					if online and name then
						local fullName = Cerebro:GetFullPlayerName(name)
						if Cerebro:IsOfficerSafe(fullName) then
							officerCount = officerCount + 1
						end
					end
				end
			end

			-- IsOfficerSafe may not work for other players via roster, so check if we're likely alone
			if officerCount <= 1 then
				Cerebro:Print("|cffff8800Warning:|r You appear to be the only officer online.")
				Cerebro:Print("After fresh start, you won't have data to sync until another officer logs in.")
				Cerebro:Print("Proceeding anyway...")
			end
		end

		-- Record cooldown timestamp
		Cerebro._freshStartCooldown = now

		-- == WIPE PHASE ==
		-- Preserve purge ledger (prevents synthetic character resurrection)
		local purgedPlayers = Cerebro.db.global.purgedPlayers

		-- Wipe local data
		Cerebro.db.global.players = {}
		Cerebro.db.global.transactions = {}
		Cerebro.db.global.audit = {}
		Cerebro.db.global.syncStatus = {}
		Cerebro.db.global.mplusRuns = {}         -- Personal M+ run history
		Cerebro.db.global.guildMplusRuns = {}    -- Guild M+ run history (officer)
		Cerebro.db.global.scope = nil  -- Triggers fresh guild lock prompt

		-- Restore config to defaults while preserving structure
		local defaultConfig = {
			epgp = {
				baseGP = 200,
				minGP = 100,
				decayRate = 10,
			},
			ui = {},
			debug = false,
			onboardingCompleted = false,  -- Reset so wizard shows on fresh start
		}
		Cerebro.db.global.config = defaultConfig

		-- Restore preserved data
		Cerebro.db.global.purgedPlayers = purgedPlayers

		-- Clear quarantine states (so sync is not blocked)
		Cerebro.db.global.epochQuarantine = nil
		Cerebro.db.global.resetQuarantine = nil

		-- == BOOTSTRAP PHASE ==
		-- Re-announce to get added back to officers' rosters
		if Cerebro.modules.CommunicationV2 and Cerebro.modules.CommunicationV2.AnnounceSelf then
			Cerebro:ScheduleTimer(function()
				Cerebro.modules.CommunicationV2:AnnounceSelf()
			end, 1)
		end

		-- Request sync from officers
		if Cerebro.modules.CommunicationV2 and Cerebro.modules.CommunicationV2.RequestSync then
			Cerebro:ScheduleTimer(function()
				Cerebro.modules.CommunicationV2:RequestSync()
			end, 2)
		end

		-- Close UI if open (will reopen with fresh state)
		if Cerebro.modules.UI and Cerebro.modules.UI.Hide then
			Cerebro.modules.UI:Hide()
		end

		Cerebro:Print("|cff00ff00Fresh start complete.|r Waiting for officer sync...")
		Cerebro:Print("Open /cb to lock to your guild and view data once synced.")
	end
}

-- Restore Backup (GM Only)
defs[#defs + 1] = {
	id = "restore_backup",
	displayName = "Restore Backup",
	description = "Restores database from last factory reset backup. Officer-only.",
	officerOnly = true, -- Base check; handler enforces GM-only
	hidden = true,
	dangerous = true,
	slash = "/cb restore YES",
	usage = "/cb restore YES",
	argsSchema = {
		{name = "confirm", type = "string", required = true, values = {"YES"}},
	},
	handler = function(ctx, argv)
		-- Officers can restore (was GM-only before v0.8.14)
		if not Cerebro:IsOfficer() then
			Cerebro:Print("|cffff0000ACCESS DENIED:|r Restore is restricted to officers only.")
			return
		end

		-- Check backup exists
		local backup = Cerebro.db.global.backup
		if not backup or not backup.timestamp then
			Cerebro:Print("|cffff0000ERROR:|r No backup found. Nothing to restore.")
			return
		end

		-- Confirmation check
		if ctx.source == "slash" and (not argv[1] or argv[1]:upper() ~= "YES") then
			local ageHours = math.floor((time() - backup.timestamp) / 3600)
			local playerCount = 0
			for _ in pairs(backup.players or {}) do playerCount = playerCount + 1 end
			local txCount = backup.transactions and #backup.transactions or 0

			Cerebro:Print("|cffff8800Backup Available:|r")
			Cerebro:Print("  Created: " .. ageHours .. " hours ago")
			Cerebro:Print("  Version: " .. (backup.version or "unknown"))
			Cerebro:Print("  Players: " .. playerCount)
			Cerebro:Print("  Transactions: " .. txCount)
			Cerebro:Print("")
			Cerebro:Print("Run |cff00ff00/cb restore YES|r to restore this backup.")
			return
		end

		-- == RESTORE PHASE ==
		-- Restore tables
		Cerebro.db.global.players = Cerebro:CopyTable(backup.players) or {}
		Cerebro.db.global.transactions = Cerebro:CopyTable(backup.transactions) or {}
		Cerebro.db.global.audit = Cerebro:CopyTable(backup.audit) or {}

		-- Restore EPGP config if present
		if backup.epgpConfig then
			Cerebro.db.global.config.epgp = Cerebro:CopyTable(backup.epgpConfig)
		end

		-- Replay history to recalculate PR
		if Cerebro.Ledger and Cerebro.Ledger.ReplayHistory then
			Cerebro.Ledger:ReplayHistory()
		end

		-- Audit: Restore performed
		if Cerebro.Ledger then
			local ageHours = math.floor((time() - backup.timestamp) / 3600)
			Cerebro.Ledger:AddAuditLog("RESTORE_PERFORMED", "Database restored from backup (" .. ageHours .. "h old) by " .. UnitName("player"))
		end

		-- Refresh UI
		Cerebro:RefreshAllUI()

		-- Restore epoch with fallback (avoid stale sync rejection)
		Cerebro.db.global.resetEpoch = backup.resetEpoch or Cerebro.db.global.resetEpoch or 0

		-- Stamp restored records with current epoch to prevent stale marking (v0.8.18)
		-- (Option 2: Restoration treats data as authoritative for current epoch)
		local currentEpoch = Cerebro.db.global.resetEpoch
		for name, data in pairs(Cerebro.db.global.players) do
			data.createdEpoch = currentEpoch
		end
		Cerebro:Debug("Stamped restored records with current epoch: " .. currentEpoch)

		-- Clear quarantine (user explicitly wants data back)
		Cerebro.db.global.resetQuarantine = nil
		if Cerebro.Ledger then
			Cerebro.Ledger:AddAuditLog("QUARANTINE_CLEAR", "Cleared via restore by " .. UnitName("player"))
		end

		-- Sync to raid
		if Cerebro.modules.CommunicationV2 then
			Cerebro.modules.CommunicationV2:SyncToRaid()
		end

		Cerebro:Print("|cff00ff00Restore Complete.|r Database recovered from backup.")
	end
}

-- Backup Status (Officer)
defs[#defs + 1] = {
	id = "backup_status",
	displayName = "Backup Status",
	description = "Shows the status of the last factory reset backup.",
	officerOnly = true,
	hidden = false,
	dangerous = false,
	slash = "/cb backup",
	usage = "/cb backup",
	handler = function(ctx, argv)
		local backup = Cerebro.db.global.backup

		if not backup or not backup.timestamp then
			Cerebro:Print("|cff888888No backup exists.|r")
			Cerebro:Print("A backup is created automatically before any factory reset.")
			return
		end

		local ageSeconds = time() - backup.timestamp
		local ageHours = math.floor(ageSeconds / 3600)
		local ageDays = math.floor(ageHours / 24)

		local playerCount = 0
		for _ in pairs(backup.players or {}) do playerCount = playerCount + 1 end
		local txCount = backup.transactions and #backup.transactions or 0
		local auditCount = backup.audit and #backup.audit or 0

		local isProtected = ageSeconds < (72 * 60 * 60)
		local canRestore = Cerebro:IsOfficer()

		Cerebro:Print("=== Backup Status ===")
		if ageDays > 0 then
			Cerebro:Print("  Age: " .. ageDays .. " days, " .. (ageHours % 24) .. " hours")
		else
			Cerebro:Print("  Age: " .. ageHours .. " hours")
		end
		Cerebro:Print("  Version: " .. (backup.version or "unknown"))
		Cerebro:Print("  Players: " .. playerCount)
		Cerebro:Print("  Transactions: " .. txCount)
		Cerebro:Print("  Audit Entries: " .. auditCount)
		Cerebro:Print("  Protected: " .. (isProtected and "|cff00ff00Yes (72h window)|r" or "|cff888888No|r"))
		Cerebro:Print("  Can Restore: " .. (canRestore and "|cff00ff00Yes (Officer)|r" or "|cffff0000No (Officer Only)|r"))
	end
}

-- Quarantine Status/Clear
defs[#defs + 1] = {
	id = "quarantine_status",
	displayName = "Quarantine Status",
	description = "Shows quarantine status or clears it. Use '/cb quarantine clear' to clear.",
	officerOnly = false, -- Non-officers need to check/clear their quarantine
	hidden = false,
	dangerous = false,
	aliases = {"quarantine"},
	slash = "/cb quarantine [clear]",
	usage = "/cb quarantine [clear]",
	handler = function(ctx, argv)
		local subCmd = argv[1] and argv[1]:lower() or ""

		if subCmd == "clear" then
			-- Clear quarantine
			Cerebro.db.global.resetQuarantine = nil
			if Cerebro.Ledger then
				Cerebro.Ledger:AddAuditLog("QUARANTINE_CLEAR", "Manual clear by " .. UnitName("player"))
			end
			Cerebro:Print("|cff00ff00Quarantine cleared.|r Sync is now enabled.")
			return
		end

		-- Show status
		local q = Cerebro.db.global.resetQuarantine
		local epoch = Cerebro.db.global.resetEpoch or 0

		if not q then
			Cerebro:Print("|cff00ff00No quarantine active.|r Sync is enabled.")
			Cerebro:Print("  Current epoch: " .. epoch)
		else
			local age = math.floor((time() - q.at) / 60)
			local remaining = math.max(0, 15 - age)
			local isActive = remaining > 0

			if isActive then
				Cerebro:Print("|cffff8800Quarantine active:|r " .. age .. " min elapsed, " .. remaining .. " min remaining")
			else
				Cerebro:Print("|cff888888Quarantine expired|r (was set " .. age .. " min ago)")
			end
			Cerebro:Print("  Current epoch: " .. epoch)
			Cerebro:Print("Use |cff00ff00/cb quarantine clear|r to enable sync.")
		end
	end
}

-- Purge Player (DANGEROUS - Permanent removal)
defs[#defs + 1] = {
	id = "purge_player",
	displayName = "Purge Player",
	description = "Removes a player from the roster and adds to purge ledger. They can be rediscovered via guild/group scan.",
	officerOnly = true,
	hidden = true,
	dangerous = true,
	aliases = {"purge"},
	slash = "/cb purge [player] YES",
	usage = "/cb purge [player] YES",
	argsSchema = {
		{name = "player", type = "string", required = true},
		{name = "confirm", type = "string", required = true, values = {"YES"}},
	},
	handler = function(ctx, argv)
		-- Session lock: prevent purges during active raid sessions
		if Cerebro.modules.Session and Cerebro.modules.Session:IsActive() then
			Cerebro:Print("|cffff8800Purge is locked during an active session.|r End the session first.")
			return
		end

		local player = argv[1]
		if not player then
			Cerebro:Print("Usage: /cb purge [player] YES")
			Cerebro:Print("  Removes player from roster and adds to purge ledger. Rediscoverable via guild/group.")
			return
		end

		-- Dangerous command confirmation
		if ctx.source == "slash" and (not argv[2] or argv[2]:upper() ~= "YES") then
			Cerebro:Print("|cffff8800WARNING:|r This resets " .. player .. "'s EP/GP/attendance to zero.")
			Cerebro:Print("Re-run with YES to confirm: |cff00ff00/cb purge " .. player .. " YES|r")
			return
		end

		local fullName = Cerebro:GetFullPlayerName(player)
		local inputNormalized = NormalizePlayerKey(fullName)

		-- Search case-insensitive
		local targetKey = nil
		for k in pairs(Cerebro.db.global.players) do
			if k:lower() == fullName:lower() then
				targetKey = k
				break
			end
		end

		-- Pass 2: Normalized realm matching
		if not targetKey then
			for k in pairs(Cerebro.db.global.players) do
				if NormalizePlayerKey(k) == inputNormalized then
					targetKey = k
					break
				end
			end
		end

		if not targetKey then
			Cerebro:Print("Player not found in database: " .. player)
			return
		end

		-- Delete from roster + add to purge ledger
		Cerebro:PurgePlayer(targetKey)
	end
}

defs[#defs + 1] = {
	id = "block_player",
	displayName = "Block Player",
	description = "Permanently removes a player and blocks re-sync (for corrupted/unwanted entries)",
	officerOnly = true,
	hidden = true,
	dangerous = true,
	aliases = {"block"},
	slash = "/cb block [player] YES",
	usage = "/cb block [player] YES",
	argsSchema = {
		{name = "player", type = "string", required = true},
		{name = "confirm", type = "string", required = true, values = {"YES"}},
	},
	handler = function(ctx, argv)
		local player = argv[1]
		if not player then
			Cerebro:Print("Usage: /cb block [player] YES")
			Cerebro:Print("  Permanently removes player and blocks re-sync. Use /cb unblock to reverse.")
			return
		end

		-- Dangerous command confirmation
		if ctx.source == "slash" and (not argv[2] or argv[2]:upper() ~= "YES") then
			Cerebro:Print("|cffff0000DANGEROUS:|r This permanently blocks " .. player .. " from all databases.")
			Cerebro:Print("Re-run with YES to confirm: |cff00ff00/cb block " .. player .. " YES|r")
			return
		end

		local fullName = Cerebro:GetFullPlayerName(player)
		local inputNormalized = NormalizePlayerKey(fullName)

		-- Search case-insensitive in players
		local targetKey = nil
		for k in pairs(Cerebro.db.global.players) do
			if k:lower() == fullName:lower() then
				targetKey = k
				break
			end
		end

		-- Pass 2: Normalized realm matching
		if not targetKey then
			for k in pairs(Cerebro.db.global.players) do
				if NormalizePlayerKey(k) == inputNormalized then
					targetKey = k
					break
				end
			end
		end

		-- If not in players, still allow blocking by name (for pre-emptive blocks)
		if not targetKey then
			targetKey = fullName
		end

		-- Add to blocked list
		Cerebro.db.global.blockedPlayers = Cerebro.db.global.blockedPlayers or {}
		Cerebro.db.global.blockedPlayers[targetKey] = time()

		-- Delete from players (if exists)
		if Cerebro.db.global.players[targetKey] then
			Cerebro.db.global.players[targetKey] = nil
		end

		-- Audit log
		if Cerebro.Ledger then
			Cerebro.Ledger:AddAuditLog("PLAYER_BLOCK", "Blocked: " .. targetKey)
		end

		-- Refresh UI
		Cerebro:RefreshAllUI()

		Cerebro:Print("|cffff0000Blocked|r " .. targetKey .. " — removed from database, sync will reject. Use /cb unblock to reverse.")
	end
}

defs[#defs + 1] = {
	id = "unblock_player",
	displayName = "Unblock Player",
	description = "Removes a player from the blocked list, allowing re-sync",
	officerOnly = true,
	hidden = true,
	aliases = {"unblock"},
	slash = "/cb unblock [player]",
	usage = "/cb unblock [player]",
	argsSchema = {
		{name = "player", type = "string", required = true},
	},
	handler = function(ctx, argv)
		local player = argv[1]
		if not player then
			Cerebro:Print("Usage: /cb unblock [player]")
			return
		end

		local fullName = Cerebro:GetFullPlayerName(player)
		Cerebro.db.global.blockedPlayers = Cerebro.db.global.blockedPlayers or {}

		-- Search case-insensitive
		local targetKey = nil
		for k in pairs(Cerebro.db.global.blockedPlayers) do
			if k:lower() == fullName:lower() then
				targetKey = k
				break
			end
		end

		-- Also try exact match on the raw input (for entries without realm)
		if not targetKey and Cerebro.db.global.blockedPlayers[player] then
			targetKey = player
		end

		if not targetKey then
			Cerebro:Print("Player not found in blocked list: " .. player)
			return
		end

		Cerebro.db.global.blockedPlayers[targetKey] = nil

		-- Audit log
		if Cerebro.Ledger then
			Cerebro.Ledger:AddAuditLog("PLAYER_UNBLOCK", "Unblocked: " .. targetKey)
		end

		Cerebro:Print("|cff00ff00Unblocked|r " .. targetKey .. " — player can be re-synced.")
	end
}

defs[#defs + 1] = {
	id = "blocked_list",
	displayName = "Blocked List",
	description = "Shows all blocked player entries",
	officerOnly = true,
	hidden = true,
	aliases = {"blockedlist"},
	slash = "/cb blockedlist",
	usage = "/cb blockedlist",
	handler = function()
		Cerebro.db.global.blockedPlayers = Cerebro.db.global.blockedPlayers or {}
		local count = 0
		Cerebro:Print("=== Blocked Players ===")
		for name, blockTime in pairs(Cerebro.db.global.blockedPlayers) do
			local when = date("%Y-%m-%d %H:%M", blockTime)
			Cerebro:Print("  |cffff4444" .. name .. "|r  (blocked " .. when .. ")")
			count = count + 1
		end
		if count == 0 then
			Cerebro:Print("  (none)")
		else
			Cerebro:Print("Total: " .. count .. " blocked. Use /cb unblock [name] to remove.")
		end
	end
}

-- Purge Test Players (DANGEROUS - Batch removal)
defs[#defs + 1] = {
	id = "purge_test_players",
	displayName = "Purge Test Players",
	description = "Mass-purge all synthetic test players (Test*-Realm)",
	officerOnly = true,
	hidden = true,
	dangerous = true,
	aliases = {"purgetest"},
	slash = "/cb purgetest YES",
	usage = "/cb purgetest YES",
	argsSchema = {
		{name = "confirm", type = "string", required = true, values = {"YES"}},
	},
	handler = function(ctx, argv)
		-- Dangerous command confirmation
		if ctx.source == "slash" and (not argv[1] or argv[1]:upper() ~= "YES") then
			Cerebro:Print("|cffff0000DANGEROUS:|r This purges ALL test players (Test*-Realm).")
			Cerebro:Print("Re-run with YES: |cff00ff00/cb purgetest YES|r")
			return
		end

		local purged = 0
		local names = {}

		Cerebro.db.global.purgedPlayers = Cerebro.db.global.purgedPlayers or {}

		for name in pairs(Cerebro.db.global.players) do
			if Cerebro:IsSyntheticTestPlayer(name) then
				Cerebro.db.global.purgedPlayers[name] = time()
				Cerebro.db.global.players[name] = nil
				table.insert(names, name)
				purged = purged + 1
			end
		end

		if purged > 0 then
			-- Audit log
			if Cerebro.Ledger then
				Cerebro.Ledger:AddAuditLog("PLAYER_PURGE", "Batch purged " .. purged .. " test players")
			end
			-- Sync to propagate purge ledger
			if Cerebro.modules.CommunicationV2 then
				Cerebro.modules.CommunicationV2:SyncToRaid()
			end
		end

		-- Refresh UI
		Cerebro:RefreshAllUI()

		Cerebro:Print(string.format("|cffff0000Purged %d test players.|r", purged))
	end
}

-- Purge Ghost Players (non-guild members that accumulated via tooltip/sync pollution)
defs[#defs + 1] = {
	id = "purge_ghosted",
	displayName = "Purge Ghost Players",
	description = "Mass-purge all players not in the current guild roster (tooltip pollution cleanup)",
	officerOnly = true,
	hidden = false,
	dangerous = true,
	aliases = {"purgeghosted", "purgeghosts"},
	slash = "/cb purgeghosted [YES]",
	usage = "/cb purgeghosted [YES]  (without YES = dry run)",
	argsSchema = {
		{name = "confirm", type = "string", required = false, values = {"YES"}},
	},
	handler = function(ctx, argv)
		local dryRun = not (argv[1] and argv[1]:upper() == "YES")

		-- Build guild roster lookup table
		local guildMembers = {}
		local guildCount = 0
		if IsInGuild() then
			for i = 1, GetNumGuildMembers() do
				local gName = GetGuildRosterInfo(i)
				if gName then
					local fullName = Cerebro:GetFullPlayerName(gName)
					guildMembers[fullName] = true
					guildCount = guildCount + 1
				end
			end
		end

		if guildCount == 0 then
			Cerebro:Print("|cffff0000ERROR:|r Could not load guild roster. Try opening the guild panel first (J key).")
			return
		end

		-- Find non-guild players
		local ghosts = {}
		for name, data in pairs(Cerebro.db.global.players) do
			if not guildMembers[name] then
				table.insert(ghosts, {name = name, status = data.status or "Unknown", ep = data.ep or 0})
			end
		end

		if #ghosts == 0 then
			Cerebro:Print("|cff00ff00No ghost players found.|r All tracked players are in the guild roster.")
			return
		end

		-- Sort by name for readability
		table.sort(ghosts, function(a, b) return a.name < b.name end)

		if dryRun then
			Cerebro:Print(string.format("|cffff8800[DRY RUN]|r Found %d ghost players (not in guild roster of %d):", #ghosts, guildCount))
			local shown = 0
			for _, g in ipairs(ghosts) do
				if shown < 20 then
					Cerebro:Print(string.format("  %s (%s, EP: %d)", g.name, g.status, g.ep))
					shown = shown + 1
				end
			end
			if #ghosts > 20 then
				Cerebro:Print(string.format("  ... and %d more", #ghosts - 20))
			end
			Cerebro:Print("")
			Cerebro:Print("Run |cff00ff00/cb purgeghosted YES|r to permanently remove these players.")
			Cerebro:Print("|cffff8800WARNING:|r This removes them from ALL officer databases via purge ledger sync.")
			return
		end

		-- Execute purge
		Cerebro.db.global.purgedPlayers = Cerebro.db.global.purgedPlayers or {}
		local purged = 0

		for _, g in ipairs(ghosts) do
			Cerebro.db.global.purgedPlayers[g.name] = time()
			Cerebro.db.global.players[g.name] = nil
			purged = purged + 1
		end

		-- Audit log
		if Cerebro.Ledger then
			Cerebro.Ledger:AddAuditLog("PLAYER_PURGE", string.format("Batch purged %d ghost (non-guild) players by %s", purged, UnitName("player")))
		end

		-- Sync purge ledger to other officers
		if Cerebro.modules.CommunicationV2 then
			Cerebro.modules.CommunicationV2:SyncToRaid()
		end

		-- Refresh UI
		Cerebro:RefreshAllUI()

		Cerebro:Print(string.format("|cffff0000Purged %d ghost players.|r Purge ledger will sync to other officers.", purged))
	end
}

-- Test Season Reset (Dev-Only)
defs[#defs + 1] = {
	id = "test_season_reset",
	displayName = "Test Season Reset",
	description = "Force season reset for testing (dev builds only)",
	officerOnly = true,
	hidden = true,
	dangerous = true,
	slash = "/cb testseasonreset",
	usage = "/cb testseasonreset",
	handler = function(ctx, argv)
		-- Dev gate
		if Cerebro:IsReleaseBuild() then
			Cerebro:Print("|cffff0000Error:|r This command is only available in dev builds.")
			return
		end

		if not Cerebro:IsOfficer() then
			Cerebro:Print("|cffff0000Error:|r Officer permission required.")
			return
		end

		-- Force reset attendance counters
		local playerCount = 0
		for name, data in pairs(Cerebro.db.global.players or {}) do
			data.bossKillsPresent = 0
			playerCount = playerCount + 1
		end

		Cerebro.db.global.seasonBossKills = 0

		if Cerebro.Ledger then
			Cerebro.Ledger:AddAuditLog("ATTENDANCE_RESET", "Manual test reset by " .. UnitName("player"))
		end

		Cerebro:Print(string.format("|cffff8800[Test]|r Reset attendance for %d players. seasonBossKills = 0", playerCount))

		-- Refresh UI
		Cerebro:RefreshAllUI()
	end
}
