-- ApiVault Event Lab + Payload Recorder. Added in v1.0.0.
-- User-driven event listening only. Does not auto-register all events.
local addonName, ApiVault = ...

ApiVault.EventRecorder = ApiVault.EventRecorder or {}
local Recorder = ApiVault.EventRecorder

Recorder.active = Recorder.active or {}
Recorder.history = Recorder.history or {}
Recorder.counts = Recorder.counts or {}
Recorder.maxActive = 16
Recorder.maxHistory = 120
Recorder.maxArgs = 16

local BLOCKED_EVENTS = {
	COMBAT_LOG_EVENT_UNFILTERED = true,
}

local function isValidEventName(eventName)
	eventName = tostring(eventName or "")
	return eventName ~= "" and string.match(eventName, "^[A-Z0-9_]+$") ~= nil
end

local function summarize(value)
	if ApiVault.ReturnSerializer and ApiVault.ReturnSerializer.SerializeValue then
		return ApiVault.ReturnSerializer:SerializeValue(value, 0, {})
	end
	if ApiVault.Serialize then
		return ApiVault:Serialize(value)
	end
	return tostring(value)
end

function Recorder:GetFrame()
	if not self.frame then
		self.frame = CreateFrame("Frame")
		self.frame:SetScript("OnEvent", function(_, event, ...)
			ApiVault.EventRecorder:Record(event, ...)
		end)
	end
	return self.frame
end

function Recorder:GetActiveCount()
	local count = 0
	for _ in pairs(self.active or {}) do count = count + 1 end
	return count
end

function Recorder:Start(eventName)
	eventName = tostring(eventName or "")
	if not isValidEventName(eventName) then
		return false, "Invalid event name. Use uppercase event literals such as BAG_UPDATE."
	end
	if BLOCKED_EVENTS[eventName] then
		return false, "Blocked event: " .. tostring(eventName) .. ". ApiVault does not register combat log event listeners."
	end
	if self.active[eventName] then
		return true, "Already listening to " .. eventName
	end
	if self:GetActiveCount() >= self.maxActive then
		return false, "Active event cap reached: " .. tostring(self.maxActive)
	end
	local frame = self:GetFrame()
	local ok, err = pcall(frame.RegisterEvent, frame, eventName)
	if not ok then
		return false, tostring(err)
	end
	self.active[eventName] = true
	return true, "Listening to " .. eventName
end

function Recorder:Stop(eventName)
	eventName = tostring(eventName or "")
	if not self.active[eventName] then
		return true, "Not listening to " .. eventName
	end
	local frame = self:GetFrame()
	pcall(frame.UnregisterEvent, frame, eventName)
	self.active[eventName] = nil
	return true, "Stopped " .. eventName
end

function Recorder:StopAll()
	local frame = self:GetFrame()
	for eventName in pairs(self.active or {}) do
		pcall(frame.UnregisterEvent, frame, eventName)
	end
	self.active = {}
	return true, "Stopped all recorded events."
end

function Recorder:Clear()
	self.history = {}
	self.counts = {}
end

function Recorder:Record(event, ...)
	local now = GetTime and GetTime() or 0
	self.counts[event] = (self.counts[event] or 0) + 1
	local record = {
		event = event,
		time = now,
		count = self.counts[event],
		args = {},
	}
	local n = select("#", ...)
	for index = 1, math.min(n, self.maxArgs) do
		table.insert(record.args, summarize(select(index, ...)))
	end
	if n > self.maxArgs then
		table.insert(record.args, "<payload truncated: " .. tostring(n) .. " args>")
	end
	table.insert(self.history, 1, record)
	while #self.history > self.maxHistory do
		table.remove(self.history)
	end
	if self.onUpdate then
		self.onUpdate()
	end
end

function Recorder:GetActiveList()
	local list = {}
	for eventName in pairs(self.active or {}) do table.insert(list, eventName) end
	table.sort(list)
	return list
end

function Recorder:FormatLog()
	local lines = {}
	table.insert(lines, "ApiVault Event Lab + Payload Recorder")
	table.insert(lines, "Policy: user-driven event listener; no auto-register-all behavior.")
	table.insert(lines, "Active: " .. table.concat(self:GetActiveList(), ", "))
	table.insert(lines, "History: " .. tostring(#(self.history or {})) .. " / " .. tostring(self.maxHistory))
	table.insert(lines, "")
	for index, record in ipairs(self.history or {}) do
		table.insert(lines, string.format("%03d. %s  count=%s  time=%.3f", index, tostring(record.event), tostring(record.count), tonumber(record.time or 0)))
		for argIndex, value in ipairs(record.args or {}) do
			table.insert(lines, "     [" .. tostring(argIndex) .. "] " .. tostring(value))
		end
	end
	return table.concat(lines, "\n")
end

function Recorder:BuildListenerSnippet(eventName)
	eventName = tostring(eventName or "EVENT_NAME")
	if not isValidEventName(eventName) then eventName = "EVENT_NAME" end
	return table.concat({
		'local frame = CreateFrame("Frame")',
		'frame:RegisterEvent("' .. eventName .. '")',
		'frame:SetScript("OnEvent", function(self, event, ...)',
		'    print(event, ...)',
		'end)',
	}, "\n")
end

function Recorder:GetStats()
	return self:GetActiveCount(), #(self.history or {}), self.maxHistory
end
