--[[
	Cerebro - Command Registry
	Central registry for all addon commands with RBAC enforcement

	Command definitions have been decomposed into domain files (Phase 2a):
	  UI/Commands/UICommands.lua   — UI toggle, help, config, discord export
	  UI/Commands/Diagnostic.lua   — status, selftest, support, debug
	  UI/Commands/Session.lua      — start/end session, prep check, attendance
	  UI/Commands/Sync.lua         — sync, version check, hash debug
	  UI/Commands/Import.lua       — RCLC import, EP import, loot history
	  UI/Commands/EPGP.lua         — award, subtract, readiness
	  UI/Commands/Testing.lua      — test commands (/testcalc, etc.)
	  UI/Commands/Admin.lua        — admit, delete, purge, reset, factory reset

	Each domain file appends its commands to Cerebro._commandDefs during load.
	This file aggregates them and provides GetAll/GetFiltered/Execute.

	@module UI.CommandRegistry
	@version 0.12.0
	@invariant All commands registered via Cerebro._commandDefs aggregation
	@invariant RBAC enforced at Execute() via IsOfficerSafe() for officerOnly commands
]]--

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

-- Command Registry Module
local CommandRegistry = {}

-- Aggregate commands from domain files (loaded earlier in TOC)
local commands = Cerebro._commandDefs or {}


-- Get all commands
function CommandRegistry:GetAll()
	return commands
end

-- Get filtered commands (by search text)
function CommandRegistry:GetFiltered(searchText)
	if not searchText or searchText == "" then
		return commands
	end

	local filtered = {}
	local search = searchText:lower()

	for _, cmd in ipairs(commands) do
		local match = false

		if cmd.displayName and cmd.displayName:lower():find(search, 1, true) then
			match = true
		elseif cmd.description and cmd.description:lower():find(search, 1, true) then
			match = true
		elseif cmd.slash and cmd.slash:lower():find(search, 1, true) then
			match = true
		end

		if match then
			table.insert(filtered, cmd)
		end
	end

	return filtered
end

-- Execute command with RBAC enforcement and context
function CommandRegistry:Execute(cmdId, argv, ctx)
	argv = argv or {}
	ctx = ctx or {}

	-- Resolve alias to ID
	local resolvedId = cmdId
	for _, cmd in ipairs(commands) do
		if cmd.id == cmdId then
			resolvedId = cmdId
			break
		end
		if cmd.aliases then
			for _, alias in ipairs(cmd.aliases) do
				if alias == cmdId then
					resolvedId = cmd.id
					break
				end
			end
		end
	end

	-- Find command
	local cmd = nil
	for _, c in ipairs(commands) do
		if c.id == resolvedId then
			cmd = c
			break
		end
	end

	if not cmd then
		return false, "Command not found: " .. tostring(cmdId)
	end

	-- RBAC check (click-time for UI, always for slash)
	if cmd.officerOnly and not Cerebro:IsOfficerSafe() then
		Cerebro:Print("This command requires officer permissions.")
		return false, "Permission denied"
	end

	-- Execute via pcall
	local success, result = pcall(function()
		cmd.handler(ctx, argv)
	end)

	if not success then
		Cerebro:Debug("CommandRegistry: Execute failed for " .. resolvedId .. ": " .. tostring(result))
		return false, tostring(result)
	end

	return true, nil
end

-- Make globally accessible
_G.UI_CommandRegistry = CommandRegistry

-- Register test slash commands (proxies to test files if loaded)
-- These ensure /testcalc etc. exist even if test files fail to load
SLASH_TESTCALC1 = "/testcalc"
SlashCmdList["TESTCALC"] = function()
	if _G.RunPlayerCalcTests then
		_G.RunPlayerCalcTests()
	else
		Cerebro:Print("Calc tests not loaded. Ensure tests are included in TOC.")
	end
end

SLASH_TESTDECAY1 = "/testdecay"
SlashCmdList["TESTDECAY"] = function()
	if _G.RunDecayTests then
		_G.RunDecayTests()
	else
		Cerebro:Print("Decay tests not loaded.")
	end
end

SLASH_TESTLEDGER1 = "/testledger"
SlashCmdList["TESTLEDGER"] = function()
	if _G.RunLedgerTests then
		_G.RunLedgerTests()
	else
		Cerebro:Print("Ledger tests not loaded.")
	end
end

SLASH_TESTSESSION1 = "/testsession"
SlashCmdList["TESTSESSION"] = function()
	if _G.RunSessionTests then
		_G.RunSessionTests()
	else
		Cerebro:Print("Session tests not loaded.")
	end
end

SLASH_TESTMPLUS1 = "/testmplus"
SlashCmdList["TESTMPLUS"] = function()
	if _G.RunMPlusTests then
		_G.RunMPlusTests()
	else
		Cerebro:Print("M+ tests not loaded.")
	end
end

SLASH_TESTDIAG1 = "/testdiag"
SlashCmdList["TESTDIAG"] = function()
	if _G.RunDiagTests then
		_G.RunDiagTests()
	else
		Cerebro:Print("Diagnostic tests not loaded.")
	end
end
